diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4bd40c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +/logs/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e3ea6cb --- /dev/null +++ b/pom.xml @@ -0,0 +1,310 @@ + + + 4.0.0 + + com.jytech + riis + 2.7 + 配电网变电站智能化平台 + 配电网变电站智能化平台 + pom + + + org.springframework.boot + spring-boot-starter-parent + 2.7.3 + + + + + riis-system + riis-monitor + + + + 1.8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + org.springframework.boot + spring-boot-starter-cache + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.google.guava + guava + 30.0-jre + + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + + + org.springframework.boot + spring-boot-starter-quartz + + + + + + + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.2.2 + + + + + com.alibaba + druid + 1.2.3 + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + com.alibaba + druid-spring-boot-starter + 1.1.22 + + + + + mysql + mysql-connector-java + runtime + + + + + org.xerial + sqlite-jdbc + 3.32.3.2 + + + + + com.baomidou + mybatis-plus-boot-starter + 3.4.3 + + + + com.baomidou + mybatis-plus-generator + 3.4.1 + + + + + org.apache.commons + commons-lang3 + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + cn.hutool + hutool-all + 5.8.8 + + + + + org.apache.poi + poi + 4.1.2 + + + org.apache.poi + poi-ooxml + 4.1.2 + + + + + com.alibaba + fastjson + 1.2.70 + + + + io.springfox + springfox-boot-starter + 3.0.0 + + + com.github.xiaoymin + knife4j-spring-boot-starter + 3.0.2 + + + org.springframework.boot + spring-boot-starter-websocket + + + org.freemarker + freemarker + 2.3.28 + compile + + + org.jsoup + jsoup + 1.11.3 + + + + + com.github.ulisesbocchio + jasypt-spring-boot-starter + 1.16 + + + + org.lionsoul + ip2region + 1.7.2 + + + + + com.github.whvcse + easy-captcha + 1.6.2 + + + + + eu.bitwalker + UserAgentUtils + 1.21 + + + + + org.dom4j + dom4j + 2.1.3 + + + + + + + + src/main/resources + + **/*.* + + false + + + src/main/java + + **/*.* + + + **/*.java + + false + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.8 + + + generate-docs + prepare-package + + process-asciidoc + + + html + book + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.3 + + + + org.projectlombok + lombok + + + + + + + + diff --git a/riis-monitor/pom.xml b/riis-monitor/pom.xml new file mode 100644 index 0000000..9ba9cf0 --- /dev/null +++ b/riis-monitor/pom.xml @@ -0,0 +1,252 @@ + + + 4.0.0 + + riis-monitor + 视频监控模块 + war + + + com.jytech + riis + 2.7 + + + + 1.8 + + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.4.3 + + + + + javax.validation + validation-api + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + javax.sip + jain-sip-ri + 1.3.0-91 + + + + + org.slf4j + log4j-over-slf4j + 1.7.36 + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.17 + + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + + + com.squareup.okhttp3 + logging-interceptor + 4.9.3 + + + + + io.github.rburgst + okhttp-digest + 2.7 + + + + + org.springdoc + springdoc-openapi-ui + 1.6.10 + + + + com.github.xiaoymin + knife4j-springdoc-ui + 3.0.3 + + + + + org.bitbucket.b_c + jose4j + 0.9.3 + + + + + org.mitre.dsmiley.httpproxy + smiley-http-proxy-servlet + 1.12.1 + + + + + com.alibaba + easyexcel + 3.1.1 + + + + com.google.guava + guava + 31.1-jre + + + + + com.github.oshi + oshi-core + 6.2.2 + + + + com.github.pagehelper + pagehelper + 5.3.1 + compile + + + org.dom4j + dom4j + 2.1.3 + compile + + + com.alibaba.fastjson2 + fastjson2 + 2.0.17 + compile + + + com.alibaba + easyexcel-core + 3.1.1 + compile + + + org.jetbrains + annotations + 22.0.0 + compile + + + + org.junit.jupiter + junit-jupiter-api + + + com.alibaba.fastjson2 + fastjson2-extension + 2.0.17 + compile + + + org.bytedeco.javacpp-presets + ffmpeg + 4.0.2-1.4.3 + compile + + + + + + + + + src/main/resources + + + src/main/java + + **/*.xml + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.8 + + + generate-docs + prepare-package + + process-asciidoc + + + html + book + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.3 + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + true + + + + + + + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/MonitorApplication.java b/riis-monitor/src/main/java/com/yfd/monitor/MonitorApplication.java new file mode 100644 index 0000000..07c9b71 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/MonitorApplication.java @@ -0,0 +1,70 @@ +package com.yfd.monitor; + +import com.yfd.monitor.utils.GitUtil; +import com.yfd.monitor.utils.SpringBeanFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import java.util.Collections; + +/** + * 启动类 + */ +@ServletComponentScan("com.yfd.monitor.conf") +@SpringBootApplication +@EnableScheduling +@EnableWebMvc +public class MonitorApplication extends SpringBootServletInitializer { + + private final static Logger logger = LoggerFactory.getLogger(MonitorApplication.class); + + private static String[] args; + private static ConfigurableApplicationContext context; + public static void main(String[] args) { + MonitorApplication.args = args; + MonitorApplication.context = SpringApplication.run(MonitorApplication.class, args); + GitUtil gitUtil1 = SpringBeanFactory.getBean("gitUtil"); + logger.info("构建版本: {}", gitUtil1.getBuildVersion()); + logger.info("构建时间: {}", gitUtil1.getBuildDate()); + logger.info("GIT最后提交时间: {}", gitUtil1.getCommitTime()); + } + // 项目重启 + public static void restart() { + context.close(); + MonitorApplication.context = SpringApplication.run(MonitorApplication.class, args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(MonitorApplication.class); + } + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + super.onStartup(servletContext); + + servletContext.setSessionTrackingModes( + Collections.singleton(SessionTrackingMode.COOKIE) + ); + SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig(); + sessionCookieConfig.setHttpOnly(true); + + } + + static { + System.setProperty("druid.mysql.usePingMethod","false"); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/ApiSaveConstant.java b/riis-monitor/src/main/java/com/yfd/monitor/common/ApiSaveConstant.java new file mode 100644 index 0000000..e57555d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/ApiSaveConstant.java @@ -0,0 +1,197 @@ +package com.yfd.monitor.common; + +/** + * 为API重命名, 方便向数据库记录数据的时候展示 + */ +public class ApiSaveConstant { + + public static String getVal(String key) { + String[] keyItemArray = key.split("/"); + if (keyItemArray.length <= 1 || !"api".equals(keyItemArray[1])) { + return null; + } + if (keyItemArray.length >= 4) { + switch (keyItemArray[2]) { + case "alarm": + if ("delete".equals(keyItemArray[3])) { + return "删除报警"; + } + break; + case "device": + switch (keyItemArray[3]) { + case "config": + if (keyItemArray.length >= 5 && "basicParam".equals(keyItemArray[4])) { + return "[设备配置] 基本配置设置命令"; + } + break; + case "control": + switch (keyItemArray[4]) { + case "teleboot": + return "[设备控制] 远程启动"; + case "record": + return "[设备控制] 录像控制"; + case "guard": + return "[设备控制] 布防/撤防命令"; + case "reset_alarm": + return "[设备控制] 报警复位"; + case "i_frame": + return "[设备控制] 强制关键帧"; + case "home_position": + return "[设备控制] 看守位控制"; + default: + return ""; + } + case "query": + if (keyItemArray.length <= 5) { + return null; + } + switch (keyItemArray[4]) { + case "devices": + if (keyItemArray.length < 7) { + return null; + } + switch (keyItemArray[6]) { + case "sync": + return "[设备查询] 同步设备通道"; + case "delete": + return "[设备查询] 移除设备"; + default: + return ""; + } + case "channel": + return "[设备查询] 更新通道信息"; + case "transport": + return "[设备查询] 修改数据流传输模式"; + default: + return ""; + } + default: + return ""; + } + + break; + case "gbStream": + switch (keyItemArray[3]) { + case "del": + return "移除通道与国标的关联"; + case "add": + return "添加通道与国标的关联"; + default: + return ""; + } + case "media": + break; + case "position": + if ("subscribe".equals(keyItemArray[3])) { + return "订阅位置信息"; + } + break; + case "platform": + switch (keyItemArray[3]) { + case "save": + return "添加上级平台"; + case "delete": + return "移除上级平台"; + case "update_channel_for_gb": + return "向上级平台添加国标通道"; + case "del_channel_for_gb": + return "从上级平台移除国标通道"; + default: + return ""; + } + case "platform_gb_stream": + break; + case "play": + switch (keyItemArray[3]) { + case "start": + return "开始点播"; + case "stop": + return "停止点播"; + case "convert": + return "转码"; + case "convertStop": + return "结束转码"; + case "broadcast": + return "语音广播"; + default: + return ""; + } + case "download": + switch (keyItemArray[3]) { + case "start": + return "开始历史媒体下载"; + case "stop": + return "停止历史媒体下载"; + default: + return ""; + } + case "playback": + switch (keyItemArray[3]) { + case "start": + return "开始视频回放"; + case "stop": + return "停止视频回放"; + default: + return ""; + } + case "ptz": + switch (keyItemArray[3]) { + case "control": + return "云台控制"; + case "front_end_command": + return "通用前端控制命令"; + default: + return ""; + } + case "gb_record": + break; + case "onvif": + break; + case "server": + if ("restart".equals(keyItemArray[3])) { + return "重启流媒体服务"; + } + break; + case "proxy": + switch (keyItemArray[3]) { + case "save": + return "保存代理"; + case "del": + return "移除代理"; + case "start": + return "启用代理"; + case "stop": + return "停用代理"; + default: + return ""; + } + case "push": + switch (keyItemArray[3]) { + case "save_to_gb": + return "将推流添加到国标"; + case "remove_form_gb": + return "将推流移出到国标"; + default: + return ""; + } + case "user": + switch (keyItemArray[3]) { + case "login": + return "登录"; + case "changePassword": + return "修改密码"; + case "add": + return "添加用户"; + case "delete": + return "删除用户"; + default: + return ""; + } + default: + return ""; + } + } + return null; + } +} + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/CommonCallback.java b/riis-monitor/src/main/java/com/yfd/monitor/common/CommonCallback.java new file mode 100644 index 0000000..a18b2f0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/CommonCallback.java @@ -0,0 +1,5 @@ +package com.yfd.monitor.common; + +public interface CommonCallback{ + void run(T t); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/InviteInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/common/InviteInfo.java new file mode 100644 index 0000000..25ecc93 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/InviteInfo.java @@ -0,0 +1,145 @@ +package com.yfd.monitor.common; + + +import com.yfd.monitor.service.bean.SSRCInfo; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 记录每次发送invite消息的状态 + */ +public class InviteInfo { + + private String deviceId; + + private String channelId; + + private String stream; + + private SSRCInfo ssrcInfo; + + private String receiveIp; + + private Integer receivePort; + + private String streamMode; + + private InviteSessionType type; + + private InviteSessionStatus status; + + private StreamInfo streamInfo; + + + public static InviteInfo getInviteInfo(String deviceId, String channelId, String stream, SSRCInfo ssrcInfo, + String receiveIp, Integer receivePort, String streamMode, + InviteSessionType type, InviteSessionStatus status) { + InviteInfo inviteInfo = new InviteInfo(); + inviteInfo.setDeviceId(deviceId); + inviteInfo.setChannelId(channelId); + inviteInfo.setStream(stream); + inviteInfo.setSsrcInfo(ssrcInfo); + inviteInfo.setReceiveIp(receiveIp); + inviteInfo.setReceivePort(receivePort); + inviteInfo.setStreamMode(streamMode); + inviteInfo.setType(type); + inviteInfo.setStatus(status); + return inviteInfo; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public InviteSessionType getType() { + return type; + } + + public void setType(InviteSessionType type) { + this.type = type; + } + + public InviteSessionStatus getStatus() { + return status; + } + + public void setStatus(InviteSessionStatus status) { + this.status = status; + } + + public StreamInfo getStreamInfo() { + return streamInfo; + } + + public void setStreamInfo(StreamInfo streamInfo) { + this.streamInfo = streamInfo; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public SSRCInfo getSsrcInfo() { + return ssrcInfo; + } + + public void setSsrcInfo(SSRCInfo ssrcInfo) { + this.ssrcInfo = ssrcInfo; + } + + public String getReceiveIp() { + return receiveIp; + } + + public void setReceiveIp(String receiveIp) { + this.receiveIp = receiveIp; + } + + public Integer getReceivePort() { + return receivePort; + } + + public void setReceivePort(Integer receivePort) { + this.receivePort = receivePort; + } + + public String getStreamMode() { + return streamMode; + } + + public void setStreamMode(String streamMode) { + this.streamMode = streamMode; + } + + + /*=========================设备主子码流逻辑START====================*/ + @Schema(description = "是否为子码流(true-是,false-主码流)") + private boolean subStream; + + public boolean isSubStream() { + return subStream; + } + + public void setSubStream(boolean subStream) { + this.subStream = subStream; + } + + + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/InviteSessionStatus.java b/riis-monitor/src/main/java/com/yfd/monitor/common/InviteSessionStatus.java new file mode 100644 index 0000000..adaee8e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/InviteSessionStatus.java @@ -0,0 +1,11 @@ +package com.yfd.monitor.common; + +/** + * 标识invite消息发出后的各个状态, + * 收到ok钱停止invite发送cancel, + * 收到200ok后发送BYE停止invite + */ +public enum InviteSessionStatus { + ready, + ok, +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/InviteSessionType.java b/riis-monitor/src/main/java/com/yfd/monitor/common/InviteSessionType.java new file mode 100644 index 0000000..8c24b81 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/InviteSessionType.java @@ -0,0 +1,9 @@ +package com.yfd.monitor.common; + +public enum InviteSessionType { + PLAY, + PLAYBACK, + DOWNLOAD, + BROADCAST, + TALK +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/ServerSendEventServer.java b/riis-monitor/src/main/java/com/yfd/monitor/common/ServerSendEventServer.java new file mode 100644 index 0000000..712bcc7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/ServerSendEventServer.java @@ -0,0 +1,134 @@ +package com.yfd.monitor.common; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +/** + * SSE Server send Event 服务器推送服务 + */ +@Slf4j +public class ServerSendEventServer { + + /** + * 当前连接数 + */ + private static AtomicInteger count = new AtomicInteger(0); + + private static Map sseEmitterMap = new ConcurrentHashMap<>(); + + public static SseEmitter connect(String userId){ + //设置超时时间,0表示不过期,默认是30秒,超过时间未完成会抛出异常 + SseEmitter sseEmitter = new SseEmitter(0L); + //SseEmitter sseEmitter = new SseEmitter(); + //注册回调 + sseEmitter.onCompletion(completionCallBack(userId)); + sseEmitter.onError(errorCallBack(userId)); + sseEmitter.onTimeout(timeOutCallBack(userId)); + sseEmitterMap.put(userId,sseEmitter); + //数量+1 + count.getAndIncrement(); + log.info("create new sse connect ,current user:{}",userId); + return sseEmitter; + } + /** + * 给指定用户发消息 + */ + public static void sendMessage(String userId, String message){ + if(sseEmitterMap.containsKey(userId)){ + try{ + sseEmitterMap.get(userId).send(message); + }catch (IOException e){ + log.error("user id:{}, send message error:{}", userId, e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + } + + /** + * 给所有用户发消息 + */ + public static void sendMessage(String message) { + if (sseEmitterMap != null && !sseEmitterMap.isEmpty()) { + sseEmitterMap.forEach((k, v) -> { + // 发送消息 + sendMessage(k, message); + + }); + } + } + + /** + * 想多人发送消息,组播 + */ + public static void groupSendMessage(String groupId, String message){ + if(sseEmitterMap!=null&&!sseEmitterMap.isEmpty()){ + sseEmitterMap.forEach((k,v) -> { + try{ + if(k.startsWith(groupId)){ + v.send(message, MediaType.APPLICATION_JSON); + } + }catch (IOException e){ + log.error("user id:{}, send message error:{}",groupId,message); + removeUser(k); + } + }); + } + } + public static void batchSendMessage(String message) { + sseEmitterMap.forEach((k,v)->{ + try{ + v.send(message, MediaType.APPLICATION_JSON); + }catch (IOException e){ + log.error("user id:{}, send message error:{}",k,e.getMessage()); + removeUser(k); + } + }); + } + /** + * 群发消息 + */ + public static void batchSendMessage(String message, Set userIds){ + userIds.forEach(userId->sendMessage(userId,message)); + } + public static void removeUser(String userId){ + sseEmitterMap.remove(userId); + //数量-1 + count.getAndDecrement(); + log.info("remove user id:{}",userId); + } + + public static List getIds(){ + return new ArrayList<>(sseEmitterMap.keySet()); + } + public static int getUserCount(){ + return count.intValue(); + } + private static Runnable completionCallBack(String userId) { + return () -> { + log.info("结束连接,{}",userId); + removeUser(userId); + }; + } + private static Runnable timeOutCallBack(String userId){ + return ()->{ + log.info("连接超时,{}",userId); + removeUser(userId); + }; + } + private static Consumer errorCallBack(String userId){ + return throwable -> { + log.error("连接异常,{}",userId); + removeUser(userId); + }; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/StreamInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/common/StreamInfo.java new file mode 100644 index 0000000..87aacf0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/StreamInfo.java @@ -0,0 +1,544 @@ +package com.yfd.monitor.common; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serializable; +import java.util.Objects; + +@Schema(description = "流信息") +public class StreamInfo implements Serializable, Cloneable{ + + @Schema(description = "应用名") + private String app; + @Schema(description = "流ID") + private String stream; + @Schema(description = "设备编号") + private String deviceID; + @Schema(description = "通道编号") + private String channelId; + + @Schema(description = "IP") + private String ip; + + @Schema(description = "HTTP-FLV流地址") + private StreamURL flv; + + @Schema(description = "HTTPS-FLV流地址") + private StreamURL https_flv; + @Schema(description = "Websocket-FLV流地址") + private StreamURL ws_flv; + @Schema(description = "Websockets-FLV流地址") + private StreamURL wss_flv; + @Schema(description = "HTTP-FMP4流地址") + private StreamURL fmp4; + @Schema(description = "HTTPS-FMP4流地址") + private StreamURL https_fmp4; + @Schema(description = "Websocket-FMP4流地址") + private StreamURL ws_fmp4; + @Schema(description = "Websockets-FMP4流地址") + private StreamURL wss_fmp4; + @Schema(description = "HLS流地址") + private StreamURL hls; + @Schema(description = "HTTPS-HLS流地址") + private StreamURL https_hls; + @Schema(description = "Websocket-HLS流地址") + private StreamURL ws_hls; + @Schema(description = "Websockets-HLS流地址") + private StreamURL wss_hls; + @Schema(description = "HTTP-TS流地址") + private StreamURL ts; + @Schema(description = "HTTPS-TS流地址") + private StreamURL https_ts; + @Schema(description = "Websocket-TS流地址") + private StreamURL ws_ts; + @Schema(description = "Websockets-TS流地址") + private StreamURL wss_ts; + @Schema(description = "RTMP流地址") + private StreamURL rtmp; + @Schema(description = "RTMPS流地址") + private StreamURL rtmps; + @Schema(description = "RTSP流地址") + private StreamURL rtsp; + @Schema(description = "RTSPS流地址") + private StreamURL rtsps; + @Schema(description = "RTC流地址") + private StreamURL rtc; + + @Schema(description = "RTCS流地址") + private StreamURL rtcs; + @Schema(description = "流媒体ID") + private String mediaServerId; + @Schema(description = "流编码信息") + private Object tracks; + @Schema(description = "开始时间") + private String startTime; + @Schema(description = "结束时间") + private String endTime; + @Schema(description = "进度(录像下载使用)") + private double progress; + + @Schema(description = "是否暂停(录像回放使用)") + private boolean pause; + + public void setFlv(StreamURL flv) { + this.flv = flv; + } + + public void setHttps_flv(StreamURL https_flv) { + this.https_flv = https_flv; + } + + public void setWs_flv(StreamURL ws_flv) { + this.ws_flv = ws_flv; + } + + public void setWss_flv(StreamURL wss_flv) { + this.wss_flv = wss_flv; + } + + public void setFmp4(StreamURL fmp4) { + this.fmp4 = fmp4; + } + + public void setHttps_fmp4(StreamURL https_fmp4) { + this.https_fmp4 = https_fmp4; + } + + public void setWs_fmp4(StreamURL ws_fmp4) { + this.ws_fmp4 = ws_fmp4; + } + + public void setWss_fmp4(StreamURL wss_fmp4) { + this.wss_fmp4 = wss_fmp4; + } + + public void setHls(StreamURL hls) { + this.hls = hls; + } + + public void setHttps_hls(StreamURL https_hls) { + this.https_hls = https_hls; + } + + public void setWs_hls(StreamURL ws_hls) { + this.ws_hls = ws_hls; + } + + public void setWss_hls(StreamURL wss_hls) { + this.wss_hls = wss_hls; + } + + public void setTs(StreamURL ts) { + this.ts = ts; + } + + public void setHttps_ts(StreamURL https_ts) { + this.https_ts = https_ts; + } + + public void setWs_ts(StreamURL ws_ts) { + this.ws_ts = ws_ts; + } + + public void setWss_ts(StreamURL wss_ts) { + this.wss_ts = wss_ts; + } + + public void setRtmp(StreamURL rtmp) { + this.rtmp = rtmp; + } + + public void setRtmps(StreamURL rtmps) { + this.rtmps = rtmps; + } + + public void setRtsp(StreamURL rtsp) { + this.rtsp = rtsp; + } + + public void setRtsps(StreamURL rtsps) { + this.rtsps = rtsps; + } + + public void setRtc(StreamURL rtc) { + this.rtc = rtc; + } + + public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) { + if (callIdParam != null) { + callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&"); + } + String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam); + if (port > 0) { + this.rtc = new StreamURL("http", host, port, file); + } + if (sslPort > 0) { + this.rtcs = new StreamURL("https", host, sslPort, file); + } + } + + public void setRtcs(StreamURL rtcs) { + this.rtcs = rtcs; + } + + public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s%s", app, stream, callIdParam); + if (port > 0) { + this.rtmp = new StreamURL("rtmp", host, port, file); + } + if (sslPort > 0) { + this.rtmps = new StreamURL("rtmps", host, sslPort, file); + } + } + + public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s%s", app, stream, callIdParam); + if (port > 0) { + this.rtsp = new StreamURL("rtsp", host, port, file); + } + if (sslPort > 0) { + this.rtsps = new StreamURL("rtsps", host, sslPort, file); + } + } + + public void setFlv(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s.live.flv%s", app, stream, callIdParam); + if (port > 0) { + this.flv = new StreamURL("http", host, port, file); + } + this.ws_flv = new StreamURL("ws", host, port, file); + if (sslPort > 0) { + this.https_flv = new StreamURL("https", host, sslPort, file); + this.wss_flv = new StreamURL("wss", host, sslPort, file); + } + } + + public void setFmp4(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s.live.mp4%s", app, stream, callIdParam); + if (port > 0) { + this.fmp4 = new StreamURL("http", host, port, file); + this.ws_fmp4 = new StreamURL("ws", host, port, file); + } + if (sslPort > 0) { + this.https_fmp4 = new StreamURL("https", host, sslPort, file); + this.wss_fmp4 = new StreamURL("wss", host, sslPort, file); + } + } + + public void setHls(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s/hls.m3u8%s", app, stream, callIdParam); + if (port > 0) { + this.hls = new StreamURL("http", host, port, file); + this.ws_hls = new StreamURL("ws", host, port, file); + } + if (sslPort > 0) { + this.https_hls = new StreamURL("https", host, sslPort, file); + this.wss_hls = new StreamURL("wss", host, sslPort, file); + } + } + + public void setTs(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s.live.ts%s", app, stream, callIdParam); + + if (port > 0) { + this.ts = new StreamURL("http", host, port, file); + this.ws_ts = new StreamURL("ws", host, port, file); + } + if (sslPort > 0) { + this.https_ts = new StreamURL("https", host, sslPort, file); + this.wss_ts = new StreamURL("wss", host, sslPort, file); + } + } + + public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) { + if (callIdParam != null) { + callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&"); + } + String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam); + if (port > 0) { + this.rtc = new StreamURL("http", host, port, file); + } + if (sslPort > 0) { + this.rtcs = new StreamURL("https", host, sslPort, file); + } + } + + public void channgeStreamIp(String localAddr) { + if (this.flv != null) { + this.flv.setHost(localAddr); + } + if (this.ws_flv != null ){ + this.ws_flv.setHost(localAddr); + } + if (this.hls != null ) { + this.hls.setHost(localAddr); + } + if (this.ws_hls != null ) { + this.ws_hls.setHost(localAddr); + } + if (this.ts != null ) { + this.ts.setHost(localAddr); + } + if (this.ws_ts != null ) { + this.ws_ts.setHost(localAddr); + } + if (this.fmp4 != null ) { + this.fmp4.setHost(localAddr); + } + if (this.ws_fmp4 != null ) { + this.ws_fmp4.setHost(localAddr); + } + if (this.rtc != null ) { + this.rtc.setHost(localAddr); + } + if (this.https_flv != null) { + this.https_flv.setHost(localAddr); + } + if (this.wss_flv != null) { + this.wss_flv.setHost(localAddr); + } + if (this.https_hls != null) { + this.https_hls.setHost(localAddr); + } + if (this.wss_hls != null) { + this.wss_hls.setHost(localAddr); + } + if (this.wss_ts != null) { + this.wss_ts.setHost(localAddr); + } + if (this.https_fmp4 != null) { + this.https_fmp4.setHost(localAddr); + } + if (this.wss_fmp4 != null) { + this.wss_fmp4.setHost(localAddr); + } + if (this.rtcs != null) { + this.rtcs.setHost(localAddr); + } + if (this.rtsp != null) { + this.rtsp.setHost(localAddr); + } + if (this.rtsps != null) { + this.rtsps.setHost(localAddr); + } + if (this.rtmp != null) { + this.rtmp.setHost(localAddr); + } + if (this.rtmps != null) { + this.rtmps.setHost(localAddr); + } + } + + + public static class TransactionInfo{ + public String callId; + public String localTag; + public String remoteTag; + public String branch; + } + + private TransactionInfo transactionInfo; + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getDeviceID() { + return deviceID; + } + + public void setDeviceID(String deviceID) { + this.deviceID = deviceID; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public StreamURL getFlv() { + return flv; + } + + public StreamURL getHttps_flv() { + return https_flv; + } + + public StreamURL getWs_flv() { + return ws_flv; + } + + + public StreamURL getWss_flv() { + return wss_flv; + } + + public StreamURL getFmp4() { + return fmp4; + } + + + + public StreamURL getHttps_fmp4() { + return https_fmp4; + } + + public StreamURL getWs_fmp4() { + return ws_fmp4; + } + + public StreamURL getWss_fmp4() { + return wss_fmp4; + } + + public StreamURL getHls() { + return hls; + } + + + public StreamURL getHttps_hls() { + return https_hls; + } + + public StreamURL getWs_hls() { + return ws_hls; + } + + public StreamURL getWss_hls() { + return wss_hls; + } + + public StreamURL getTs() { + return ts; + } + + + public StreamURL getHttps_ts() { + return https_ts; + } + + + public StreamURL getWs_ts() { + return ws_ts; + } + + + public StreamURL getWss_ts() { + return wss_ts; + } + + + public StreamURL getRtmp() { + return rtmp; + } + + public StreamURL getRtmps() { + return rtmps; + } + + public StreamURL getRtsp() { + return rtsp; + } + + public StreamURL getRtsps() { + return rtsps; + } + + public StreamURL getRtc() { + return rtc; + } + + public StreamURL getRtcs() { + return rtcs; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public Object getTracks() { + return tracks; + } + + public void setTracks(Object tracks) { + this.tracks = tracks; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public double getProgress() { + return progress; + } + + public void setProgress(double progress) { + this.progress = progress; + } + + public boolean isPause() { + return pause; + } + + public void setPause(boolean pause) { + this.pause = pause; + } + + public TransactionInfo getTransactionInfo() { + return transactionInfo; + } + + public void setTransactionInfo(TransactionInfo transactionInfo) { + this.transactionInfo = transactionInfo; + } + + @Override + public StreamInfo clone() { + StreamInfo instance = null; + try{ + instance = (StreamInfo)super.clone(); + }catch(CloneNotSupportedException e) { + throw new RuntimeException(e.getMessage()); + } + return instance; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/StreamURL.java b/riis-monitor/src/main/java/com/yfd/monitor/common/StreamURL.java new file mode 100644 index 0000000..a663018 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/StreamURL.java @@ -0,0 +1,80 @@ +package com.yfd.monitor.common; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serializable; + + +@Schema(description = "流地址信息") +public class StreamURL implements Serializable { + + @Schema(description = "协议") + private String protocol; + + @Schema(description = "主机地址") + private String host; + + @Schema(description = "端口") + private int port = -1; + + @Schema(description = "定位位置") + private String file; + + @Schema(description = "拼接后的地址") + private String url; + + public StreamURL() { + } + + public StreamURL(String protocol, String host, int port, String file) { + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getUrl() { + return this.toString(); + } + + @Override + public String toString() { + if (protocol != null && host != null && port != -1 ) { + return String.format("%s://%s:%s/%s", protocol, host, port, file); + }else { + return null; + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/SystemAllInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/common/SystemAllInfo.java new file mode 100644 index 0000000..59c100e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/SystemAllInfo.java @@ -0,0 +1,54 @@ +package com.yfd.monitor.common; + +import java.util.List; + +public class SystemAllInfo { + + private List cpu; + private List mem; + private List net; + + private long netTotal; + + private Object disk; + + public List getCpu() { + return cpu; + } + + public void setCpu(List cpu) { + this.cpu = cpu; + } + + public List getMem() { + return mem; + } + + public void setMem(List mem) { + this.mem = mem; + } + + public List getNet() { + return net; + } + + public void setNet(List net) { + this.net = net; + } + + public Object getDisk() { + return disk; + } + + public void setDisk(Object disk) { + this.disk = disk; + } + + public long getNetTotal() { + return netTotal; + } + + public void setNetTotal(long netTotal) { + this.netTotal = netTotal; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/VersionPo.java b/riis-monitor/src/main/java/com/yfd/monitor/common/VersionPo.java new file mode 100644 index 0000000..ae6a937 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/VersionPo.java @@ -0,0 +1,149 @@ +package com.yfd.monitor.common; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class VersionPo { + /** + * git的全版本号 + */ + @JSONField(name="GIT_Revision") + private String GIT_Revision; + /** + * maven版本 + */ + @JSONField(name = "Create_By") + private String Create_By; + /** + * git的分支 + */ + @JSONField(name = "GIT_BRANCH") + private String GIT_BRANCH; + /** + * git的url + */ + @JSONField(name = "GIT_URL") + private String GIT_URL; + /** + * 构建日期 + */ + @JSONField(name = "BUILD_DATE") + private String BUILD_DATE; + /** + * 构建日期 + */ + @JSONField(name = "GIT_DATE") + private String GIT_DATE; + /** + * 项目名称 配合pom使用 + */ + @JSONField(name = "artifactId") + private String artifactId; + /** + * git局部版本号 + */ + @JSONField(name = "GIT_Revision_SHORT") + private String GIT_Revision_SHORT; + /** + * 项目的版本如2.0.1.0 配合pom使用 + */ + @JSONField(name = "version") + private String version; + /** + * 子系统名称 + */ + @JSONField(name = "project") + private String project; + /** + * jdk版本 + */ + @JSONField(name="Build_Jdk") + private String Build_Jdk; + + public void setGIT_Revision(String GIT_Revision) { + this.GIT_Revision = GIT_Revision; + } + + public void setCreate_By(String create_By) { + Create_By = create_By; + } + + public void setGIT_BRANCH(String GIT_BRANCH) { + this.GIT_BRANCH = GIT_BRANCH; + } + + public void setGIT_URL(String GIT_URL) { + this.GIT_URL = GIT_URL; + } + + public void setBUILD_DATE(String BUILD_DATE) { + this.BUILD_DATE = BUILD_DATE; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setGIT_Revision_SHORT(String GIT_Revision_SHORT) { + this.GIT_Revision_SHORT = GIT_Revision_SHORT; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setProject(String project) { + this.project = project; + } + + public void setBuild_Jdk(String build_Jdk) { + Build_Jdk = build_Jdk; + } + + public String getGIT_Revision() { + return GIT_Revision; + } + + public String getCreate_By() { + return Create_By; + } + + public String getGIT_BRANCH() { + return GIT_BRANCH; + } + + public String getGIT_URL() { + return GIT_URL; + } + + public String getBUILD_DATE() { + return BUILD_DATE; + } + + public String getArtifactId() { + return artifactId; + } + + public String getGIT_Revision_SHORT() { + return GIT_Revision_SHORT; + } + + public String getVersion() { + return version; + } + + public String getProject() { + return project; + } + + public String getBuild_Jdk() { + return Build_Jdk; + } + + public String getGIT_DATE() { + return GIT_DATE; + } + + public void setGIT_DATE(String GIT_DATE) { + this.GIT_DATE = GIT_DATE; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/VideoManagerConstants.java b/riis-monitor/src/main/java/com/yfd/monitor/common/VideoManagerConstants.java new file mode 100644 index 0000000..478b40f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/VideoManagerConstants.java @@ -0,0 +1,169 @@ +package com.yfd.monitor.common; + +/** + * @description: 定义常量 + * @date: 2019年5月30日 下午3:04:04 + * + */ +public class VideoManagerConstants { + + public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_"; + + public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_"; + + public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_"; + + public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_"; + public static final String ONLINE_MEDIA_SERVERS_PREFIX = "VMP_ONLINE_MEDIA_SERVERS:"; + + public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM"; + + public static final String DEVICE_PREFIX = "VMP_DEVICE_"; + + // 设备同步完成 + public static final String DEVICE_SYNC_PREFIX = "VMP_DEVICE_SYNC_"; + + public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_"; + + public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_"; + + // TODO 此处多了一个_,暂不修改 + public static final String INVITE_PREFIX = "VMP_INVITE"; + public static final String PLAYER_PREFIX = "VMP_PLAYER_"; + public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_"; + public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_"; + + public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_"; + + public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_"; + + public static final String PLATFORM_WWW_PREFIX = "VMP_PLATFORM_WWW_"; + + public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_"; + + public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_"; + + public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_"; + + public static final String EVENT_ONLINE_REGISTER = "1"; + + public static final String EVENT_ONLINE_MESSAGE = "3"; + + public static final String EVENT_OUTLINE_UNREGISTER = "1"; + + public static final String EVENT_OUTLINE_TIMEOUT = "2"; + + public static final String MEDIA_SSRC_USED_PREFIX = "VMP_MEDIA_USED_SSRC_"; + + public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; + + public static final String MEDIA_STREAM_AUTHORITY = "MEDIA_STREAM_AUTHORITY_"; + + public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_"; + + public static final String SIP_SN_PREFIX = "VMP_SIP_SN_"; + + public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_"; + + public static final String SYSTEM_INFO_CPU_PREFIX = "VMP_SYSTEM_INFO_CPU_"; + + public static final String SYSTEM_INFO_MEM_PREFIX = "VMP_SYSTEM_INFO_MEM_"; + + public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_"; + + public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_"; + + public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_"; + + public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_"; + + + + + //************************** redis 消息********************************* + + /** + * 流变化的通知 + */ + public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_"; + + /** + * 接收推流设备的GPS变化通知 + */ + public static final String VM_MSG_GPS = "VM_MSG_GPS"; + + /** + * 接收推流设备的GPS变化通知 + */ + public static final String VM_MSG_PUSH_STREAM_STATUS_CHANGE = "VM_MSG_PUSH_STREAM_STATUS_CHANGE"; + /** + * 接收推流设备列表更新变化通知 + */ + public static final String VM_MSG_PUSH_STREAM_LIST_CHANGE = "VM_MSG_PUSH_STREAM_LIST_CHANGE"; + + /** + * redis 消息通知设备推流到平台 + */ + public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED"; + + + /** + * redis 消息通知平台通知设备推流结果 + */ + public static final String VM_MSG_STREAM_PUSH_RESPONSE = "VM_MSG_STREAM_PUSH_RESPONSE"; + + /** + * redis 消息请求所有的在线通道 + */ + public static final String VM_MSG_GET_ALL_ONLINE_REQUESTED = "VM_MSG_GET_ALL_ONLINE_REQUESTED"; + + /** + * 移动位置订阅通知 + */ + public static final String VM_MSG_SUBSCRIBE_MOBILE_POSITION = "mobileposition"; + + /** + * 报警订阅的通知(收到报警向redis发出通知) + */ + public static final String VM_MSG_SUBSCRIBE_ALARM = "alarm"; + + + /** + * 报警通知的发送 (收到redis发出的通知,转发给其他平台) + */ + public static final String VM_MSG_SUBSCRIBE_ALARM_RECEIVE= "alarm_receive"; + + /** + * 设备状态订阅的通知 + */ + public static final String VM_MSG_SUBSCRIBE_DEVICE_STATUS = "device"; + + + //************************** 第三方 **************************************** + + public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; + public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; + + /** + * Redis Const + * 设备录像信息结果前缀 + */ + public static final String REDIS_RECORD_INFO_RES_PRE = "GB_RECORD_INFO_RES_"; + /** + * Redis Const + * 设备录像信息结果前缀 + */ + public static final String REDIS_RECORD_INFO_RES_COUNT_PRE = "GB_RECORD_INFO_RES_COUNT:"; + + /** + * Redis Const + * 设备录像信息90天时长 + */ + public static final String REDIS_RECORD_TIMES_PRE = "DEVICERECORDTIMES_"; + + /** + * redis 消息通知上级平台开始观看流 + */ + public static final String VM_MSG_STREAM_START_PLAY_NOTIFY = "VM_MSG_STREAM_START_PLAY_NOTIFY"; + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/common/enums/DeviceControlType.java b/riis-monitor/src/main/java/com/yfd/monitor/common/enums/DeviceControlType.java new file mode 100644 index 0000000..21aa410 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/common/enums/DeviceControlType.java @@ -0,0 +1,76 @@ +package com.yfd.monitor.common.enums; + +import org.dom4j.Element; +import org.springframework.util.ObjectUtils; + + +/** + * @date 2023/01/18/ 10:09:00 + * @since 1.0 + */ +public enum DeviceControlType { + + /** + * 云台控制 + * 上下左右,预置位,扫描,辅助功能,巡航 + */ + PTZ("PTZCmd","云台控制"), + /** + * 远程启动 + */ + TELE_BOOT("TeleBoot","远程启动"), + /** + * 录像控制 + */ + RECORD("RecordCmd","录像控制"), + /** + * 布防撤防 + */ + GUARD("GuardCmd","布防撤防"), + /** + * 告警控制 + */ + ALARM("AlarmCmd","告警控制"), + /** + * 强制关键帧 + */ + I_FRAME("IFameCmd","强制关键帧"), + /** + * 拉框放大 + */ + DRAG_ZOOM_IN("DragZoomIn","拉框放大"), + /** + * 拉框缩小 + */ + DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"), + /** + * 看守位 + */ + HOME_POSITION("HomePosition","看守位"); + + private final String val; + + private final String desc; + + DeviceControlType(String val, String desc) { + this.val = val; + this.desc = desc; + } + + public String getVal() { + return val; + } + + public String getDesc() { + return desc; + } + + public static DeviceControlType typeOf(Element rootElement) { + for (DeviceControlType item : DeviceControlType.values()) { + if (!ObjectUtils.isEmpty(rootElement.element(item.val)) || !ObjectUtils.isEmpty(rootElement.elements(item.val))) { + return item; + } + } + return null; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/ApiAccessFilter.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/ApiAccessFilter.java new file mode 100644 index 0000000..7bc50e5 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/ApiAccessFilter.java @@ -0,0 +1,119 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.common.ApiSaveConstant; +import com.yfd.monitor.conf.security.SecurityUtils; +import com.yfd.monitor.service.ILogService; +import com.yfd.monitor.storager.dao.dto.LogDto; +import com.yfd.monitor.utils.DateUtil; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true) +@Component +public class ApiAccessFilter extends OncePerRequestFilter { + + private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); + + + @Autowired + private UserSetting userSetting; + + @Autowired + private ILogService logService; + + + @Override + protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { + String username = null; + if (SecurityUtils.getUserInfo() == null) { + username = servletRequest.getParameter("username"); + }else { + username = SecurityUtils.getUserInfo().getUsername(); + } + long start = System.currentTimeMillis(); // 请求进入时间 + String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI()); + + filterChain.doFilter(servletRequest, servletResponse); + + if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != null && userSetting.getLogInDatebase()) { + + LogDto logDto = new LogDto(); + logDto.setName(uriName); + if (ObjectUtils.isEmpty(username)) { + username = ""; + } + logDto.setUsername(username); + logDto.setAddress(servletRequest.getRemoteAddr()); + logDto.setResult(HttpStatus.valueOf(servletResponse.getStatus()).toString()); + logDto.setTiming(System.currentTimeMillis() - start); + logDto.setType(servletRequest.getMethod()); + logDto.setUri(servletRequest.getRequestURI()); + logDto.setCreateTime(DateUtil.getNow()); + logService.add(logDto); + + + } + } + + /** + * 获取IP地址 + * + * @param request 请求 + * @return request发起客户端的IP地址 + */ + private String getIP(HttpServletRequest request) { + if (request == null) { + return "0.0.0.0"; + } + + String Xip = request.getHeader("X-Real-IP"); + String XFor = request.getHeader("X-Forwarded-For"); + + String UNKNOWN_IP = "unknown"; + if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { + //多次反向代理后会有多个ip值,第一个ip才是真实ip + int index = XFor.indexOf(","); + if (index != -1) { + return XFor.substring(0, index); + } else { + return XFor; + } + } + + XFor = Xip; + if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { + return XFor; + } + + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { + XFor = request.getRemoteAddr(); + } + return XFor; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/DynamicTask.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/DynamicTask.java new file mode 100644 index 0000000..39b8909 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/DynamicTask.java @@ -0,0 +1,139 @@ +package com.yfd.monitor.conf; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.time.Instant; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * 动态定时任务 + */ +@Component +public class DynamicTask { + + private final Logger logger = LoggerFactory.getLogger(DynamicTask.class); + + private ThreadPoolTaskScheduler threadPoolTaskScheduler; + + private final Map> futureMap = new ConcurrentHashMap<>(); + private final Map runnableMap = new ConcurrentHashMap<>(); + + @PostConstruct + public void DynamicTask() { + threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(300); + threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true); + threadPoolTaskScheduler.setAwaitTerminationSeconds(10); + threadPoolTaskScheduler.initialize(); + } + + /** + * 循环执行的任务 + * @param key 任务ID + * @param task 任务 + * @param cycleForCatalog 间隔 毫秒 + * @return + */ + public void startCron(String key, Runnable task, int cycleForCatalog) { + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } + // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 + future = threadPoolTaskScheduler.scheduleAtFixedRate(task, cycleForCatalog); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); + } + } + + /** + * 延时任务 + * @param key 任务ID + * @param task 任务 + * @param delay 延时 /毫秒 + * @return + */ + public void startDelay(String key, Runnable task, int delay) { + stop(key); + + // 获取执行的时刻 + Instant startInstant = Instant.now().plusMillis(TimeUnit.MILLISECONDS.toMillis(delay)); + + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } + future = threadPoolTaskScheduler.schedule(task, startInstant); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); + } + } + + public boolean stop(String key) { + boolean result = false; + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) { + result = futureMap.get(key).cancel(false); + futureMap.remove(key); + runnableMap.remove(key); + } + return result; + } + + public boolean contains(String key) { + return futureMap.get(key) != null; + } + + public Set getAllKeys() { + return futureMap.keySet(); + } + + public Runnable get(String key) { + return runnableMap.get(key); + } + + /** + * 每五分钟检查失效的任务,并移除 + */ + @Scheduled(cron="0 0/5 * * * ?") + public void execute(){ + if (futureMap.size() > 0) { + for (String key : futureMap.keySet()) { + if (futureMap.get(key).isDone() || futureMap.get(key).isCancelled()) { + futureMap.remove(key); + runnableMap.remove(key); + } + } + } + } + + public boolean isAlive(String key) { + return futureMap.get(key) != null && !futureMap.get(key).isDone() && !futureMap.get(key).isCancelled(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/GlobalExceptionHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/GlobalExceptionHandler.java new file mode 100644 index 0000000..4c2e287 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/GlobalExceptionHandler.java @@ -0,0 +1,80 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 全局异常处理 + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + + private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 默认异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public WVPResult exceptionHandler(Exception e) { + logger.error("[全局异常]: ", e); + return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage()); + } + + /** + * 默认异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(IllegalStateException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public WVPResult exceptionHandler(IllegalStateException e) { + return WVPResult.fail(ErrorCode.ERROR400); + } + + /** + * 默认异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public WVPResult exceptionHandler(HttpRequestMethodNotSupportedException e) { + return WVPResult.fail(ErrorCode.ERROR400); + } + + + /** + * 自定义异常处理, 处理controller中返回的错误 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(ControllerException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity> exceptionHandler(ControllerException e) { + return new ResponseEntity<>(WVPResult.fail(e.getCode(), e.getMsg()), HttpStatus.OK); + } + + /** + * 登陆失败 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(BadCredentialsException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity> exceptionHandler(BadCredentialsException e) { + return new ResponseEntity<>(WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage()), HttpStatus.OK); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/GlobalResponseAdvice.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/GlobalResponseAdvice.java new file mode 100644 index 0000000..41a7348 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/GlobalResponseAdvice.java @@ -0,0 +1,65 @@ +package com.yfd.monitor.conf; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.context.annotation.Bean; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * 全局统一返回结果 + */ +@RestControllerAdvice +public class GlobalResponseAdvice implements ResponseBodyAdvice { + + + @Override + public boolean supports(@NotNull MethodParameter returnType, @NotNull Class> converterType) { + return true; + } + + + @Override + public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType, @NotNull Class> selectedConverterType, @NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) { + // 排除api文档的接口,这个接口不需要统一 + String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook"}; + for (String path : excludePath) { + if (request.getURI().getPath().startsWith(path)) { + return body; + } + } + + if (body instanceof WVPResult) { + return body; + } + + if (body instanceof ErrorCode) { + ErrorCode errorCode = (ErrorCode) body; + return new WVPResult<>(errorCode.getCode(), errorCode.getMsg(), null); + } + + if (body instanceof String) { + return JSON.toJSONString(WVPResult.success(body)); + } + + return WVPResult.success(body); + } + + /** + * 防止返回string时出错 + * @return + */ + @Bean + public HttpMessageConverters fast() { + return new HttpMessageConverters(new FastJsonHttpMessageConverter()); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/MediaConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/MediaConfig.java new file mode 100644 index 0000000..8e337db --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/MediaConfig.java @@ -0,0 +1,225 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.utils.DateUtil; +import org.junit.jupiter.api.Order; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ObjectUtils; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.regex.Pattern; + + +@Configuration("mediaConfig") +@Order(0) +public class MediaConfig{ + + private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class); + + // 修改必须配置,不再支持自动获取 + @Value("${media.id}") + private String id; + + @Value("${media.ip}") + private String ip; + + @Value("${media.hook-ip:}") + private String hookIp; + + @Value("${sip.ip}") + private String sipIp; + + @Value("${sip.domain}") + private String sipDomain; + + @Value("${media.sdp-ip:${media.ip}}") + private String sdpIp; + + @Value("${media.stream-ip:${media.ip}}") + private String streamIp; + + @Value("${media.http-port}") + private Integer httpPort; + + @Value("${media.http-ssl-port:0}") + private Integer httpSSlPort = 0; + + @Value("${media.rtmp-port:0}") + private Integer rtmpPort = 0; + + @Value("${media.rtmp-ssl-port:0}") + private Integer rtmpSSlPort = 0; + + @Value("${media.rtp-proxy-port:0}") + private Integer rtpProxyPort = 0; + + @Value("${media.rtsp-port:0}") + private Integer rtspPort = 0; + + @Value("${media.rtsp-ssl-port:0}") + private Integer rtspSSLPort = 0; + + @Value("${media.auto-config:true}") + private boolean autoConfig = true; + + @Value("${media.secret}") + private String secret; + + @Value("${media.rtp.enable}") + private boolean rtpEnable; + + @Value("${media.rtp.port-range}") + private String rtpPortRange; + + @Value("${media.record-assist-port:0}") + private Integer recordAssistPort = 0; + + public String getId() { + return id; + } + + public String getIp() { + return ip; + } + + public String getHookIp() { + if (ObjectUtils.isEmpty(hookIp)){ + return sipIp.split(",")[0]; + }else { + return hookIp; + } + + } + + public String getSipIp() { + if (sipIp == null) { + return this.ip; + }else { + return sipIp; + } + } + + public int getHttpPort() { + return httpPort; + } + + public int getHttpSSlPort() { + return httpSSlPort; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public int getRtmpSSlPort() { + return rtmpSSlPort; + } + + public int getRtpProxyPort() { + if (rtpProxyPort == null) { + return 0; + }else { + return rtpProxyPort; + } + + } + + public int getRtspPort() { + return rtspPort; + } + + public int getRtspSSLPort() { + return rtspSSLPort; + } + + public boolean isAutoConfig() { + return autoConfig; + } + + public String getSecret() { + return secret; + } + + public boolean isRtpEnable() { + return rtpEnable; + } + + public String getRtpPortRange() { + return rtpPortRange; + } + + public int getRecordAssistPort() { + return recordAssistPort; + } + + public String getSdpIp() { + if (ObjectUtils.isEmpty(sdpIp)){ + return ip; + }else { + if (isValidIPAddress(sdpIp)) { + return sdpIp; + }else { + // 按照域名解析 + String hostAddress = null; + try { + hostAddress = InetAddress.getByName(sdpIp).getHostAddress(); + } catch (UnknownHostException e) { + logger.error("[获取SDP IP]: 域名解析失败"); + } + return hostAddress; + } + } + } + + public String getStreamIp() { + if (ObjectUtils.isEmpty(streamIp)){ + return ip; + }else { + return streamIp; + } + } + + public String getSipDomain() { + return sipDomain; + } + + public MediaServerItem getMediaSerItem(){ + MediaServerItem mediaServerItem = new MediaServerItem(); + mediaServerItem.setId(id); + mediaServerItem.setIp(ip); + mediaServerItem.setDefaultServer(true); + mediaServerItem.setHookIp(getHookIp()); + mediaServerItem.setSdpIp(getSdpIp()); + mediaServerItem.setStreamIp(getStreamIp()); + mediaServerItem.setHttpPort(httpPort); + mediaServerItem.setHttpSSlPort(httpSSlPort); + mediaServerItem.setRtmpPort(rtmpPort); + mediaServerItem.setRtmpSSlPort(rtmpSSlPort); + mediaServerItem.setRtpProxyPort(getRtpProxyPort()); + mediaServerItem.setRtspPort(rtspPort); + mediaServerItem.setRtspSSLPort(rtspSSLPort); + mediaServerItem.setAutoConfig(autoConfig); + mediaServerItem.setSecret(secret); + mediaServerItem.setRtpEnable(rtpEnable); + mediaServerItem.setRtpPortRange(rtpPortRange); + mediaServerItem.setRecordAssistPort(recordAssistPort); + mediaServerItem.setHookAliveInterval(30.00f); + + mediaServerItem.setCreateTime(DateUtil.getNow()); + mediaServerItem.setUpdateTime(DateUtil.getNow()); + + return mediaServerItem; + } + + private boolean isValidIPAddress(String ipAddress) { + if ((ipAddress != null) && (!ipAddress.isEmpty())) { + return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress); + } + return false; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/MediaStatusTimerTask.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/MediaStatusTimerTask.java new file mode 100644 index 0000000..3ad25de --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/MediaStatusTimerTask.java @@ -0,0 +1,15 @@ +package com.yfd.monitor.conf; + +import org.springframework.scheduling.annotation.Scheduled; + +/** + * 定时向zlm同步媒体流状态 + */ +public class MediaStatusTimerTask { + + +// @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 + public void execute(){ + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/MyCustomTask.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/MyCustomTask.java new file mode 100644 index 0000000..b95f977 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/MyCustomTask.java @@ -0,0 +1,89 @@ +package com.yfd.monitor.conf; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.DeviceMapper; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 动态定时任务 + */ +@Component +public class MyCustomTask { + @Autowired + private SIPCommander cmder; + + @Autowired + private DeviceChannelMapper channelMapper; + @Autowired + private DeviceMapper deviceMapper; + + private static final Logger logger = LoggerFactory.getLogger(MyCustomTask.class); + /** + * 每天中午定时执行,更新录像时长 0 50 13 * 6 1 0 30 12 * * ? + */ + @Scheduled(cron=" 0 30 12 * * ?") + public void updateRecordTimes(){ + List channels= channelMapper.queryChannelByOnLineNvr(); + String endTime= DateUtil.now(); + String startTime=DateUtil.format(DateUtil.offsetDay(DateUtil.date(),-90),"yyyy-MM-dd HH:mm:ss"); + try { + for (DeviceChannel channel : channels) { + Device device = deviceMapper.getDeviceByDeviceId(channel.getDeviceId());//获取录像机 + cmder.recordInfoQuery(device, channel.getChannelId(), startTime, endTime, 9999, null, null, null, null); + } + + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + /** + * 每分钟定时检测摄像头是否需要回归预置位 + */ + @Scheduled(cron="10 0/1 * * * ?") + public void callPresetPos(){ + List devices= deviceMapper.getRiisPatrolDevices(); + try { + for (Map map : devices) { + if(ObjUtil.isNotEmpty(map.get("preset")) && ObjUtil.isNotEmpty(map.get("presettime")) && ObjUtil.isNotEmpty(map.get("changepresettime"))){ + int presettime=Integer.parseInt(map.get("presettime").toString()); + String changepresettime=map.get("changepresettime").toString(); + Date nextdate=DateUtil.offsetMinute(DateUtil.parse(changepresettime,"yyyy-MM-dd HH:mm:ss"),presettime); + logger.info("nextdate:"+DateUtil.format(nextdate,"yyyy-MM-dd HH:mm:ss")); + logger.info("nowdate:"+DateUtil.format(DateUtil.date(),"yyyy-MM-dd HH:mm:ss")); + if(DateUtil.date().isAfter(nextdate)){ + Device patroldevice = deviceMapper.getDeviceByDeviceId(map.get("patroldevice_code").toString()); + List channels= channelMapper.queryAllChannels(map.get("patroldevice_code").toString());//获取一个默认通道 + if(channels.size()>0){ + cmder.frontEndCmd(patroldevice,channels.get(0).getChannelId() , 130, 0, Integer.parseInt(map.get("preset").toString()),0); + deviceMapper.updatePatroldeviceTime(map.get("patroldevice_code").toString(),""); + } + } + } + } + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 调用守望位回归: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/ProxyServletConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/ProxyServletConfig.java new file mode 100644 index 0000000..7fd26df --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/ProxyServletConfig.java @@ -0,0 +1,277 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import org.apache.catalina.connector.ClientAbortException; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.junit.jupiter.api.Order; +import org.mitre.dsmiley.httpproxy.ProxyServlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ObjectUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.net.ConnectException; + + +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Configuration +@Order(1) +public class ProxyServletConfig { + + private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class); + + @Autowired + private IMediaServerService mediaServerService; + + @Value("${server.port}") + private int serverPort; + + @Bean + public ServletRegistrationBean zlmServletRegistrationBean(){ + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZlmProxyServlet(),"/zlm/*"); + servletRegistrationBean.setName("zlm_Proxy"); + servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080"); + servletRegistrationBean.addUrlMappings(); + if (logger.isDebugEnabled()) { + servletRegistrationBean.addInitParameter("log", "true"); + } + return servletRegistrationBean; + } + + class ZlmProxyServlet extends ProxyServlet{ + @Override + protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) { + String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); + MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); + if (mediaInfo != null) { + if (!ObjectUtils.isEmpty(queryStr)) { + queryStr += "&secret=" + mediaInfo.getSecret(); + }else { + queryStr = "secret=" + mediaInfo.getSecret(); + } + } + return queryStr; + } + + /** + * 异常处理 + */ + @Override + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){ + try { + super.handleRequestException(proxyRequest, proxyResonse, e); + } catch (ServletException servletException) { + logger.error("zlm 代理失败: ", e); + } catch (IOException ioException) { + if (ioException instanceof ConnectException) { + logger.error("zlm 连接失败"); + } else { + logger.error("zlm 代理失败: ", e); + } + } catch (RuntimeException exception){ + logger.error("zlm 代理失败: ", e); + } + } + + /** + * 对于为按照格式请求的可以直接返回404 + */ + @Override + protected String getTargetUri(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + + String uri = null; + if (mediaInfo != null) { +// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length()); + uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort()); + }else { + uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以 + } + return uri; + } + + /** + * 动态替换请求目标 + */ + @Override + protected HttpHost getTargetHost(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + HttpHost host; + if (mediaInfo != null) { + host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort()); + }else { + host = new HttpHost("127.0.0.1", serverPort); + } + return host; + + } + + /** + * 根据uri获取流媒体信息 + */ + MediaServerItem getMediaInfoByUri(String uri){ + String[] split = uri.split("/"); + String mediaServerId = split[2]; + if ("default".equalsIgnoreCase(mediaServerId)) { + return mediaServerService.getDefaultMediaServer(); + }else { + return mediaServerService.getOne(mediaServerId); + } + } + + /** + * 去掉url中的标志信息 + */ + @Override + protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + String url = super.rewriteUrlFromRequest(servletRequest); + if (mediaInfo == null) { + logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); + return url; + } + if (!ObjectUtils.isEmpty(mediaInfo.getId())) { + url = url.replace(mediaInfo.getId() + "/", ""); + } + return url.replace("default/", ""); + } + } + + @Bean + public ServletRegistrationBean recordServletRegistrationBean(){ + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new RecordProxyServlet(),"/record_proxy/*"); + servletRegistrationBean.setName("record_proxy"); + servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:18081"); + servletRegistrationBean.addUrlMappings(); + if (logger.isDebugEnabled()) { + servletRegistrationBean.addInitParameter("log", "true"); + } + return servletRegistrationBean; + } + + class RecordProxyServlet extends ProxyServlet{ + + @Override + protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) { + String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); + MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); + if (mediaInfo == null) { + return null; + } + String remoteHost = String.format("http://%s:%s", mediaInfo.getStreamIp(), mediaInfo.getRecordAssistPort()); + if (!ObjectUtils.isEmpty(queryStr)) { + queryStr += "&remoteHost=" + remoteHost; + }else { + queryStr = "remoteHost=" + remoteHost; + } + return queryStr; + } + + /** + * 异常处理 + */ + @Override + protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){ + try { + super.handleRequestException(proxyRequest, proxyResponse, e); + } catch (ServletException servletException) { + logger.error("录像服务 代理失败: ", e); + } catch (IOException ioException) { + if (ioException instanceof ConnectException) { + logger.error("录像服务 连接失败"); + }else if (ioException instanceof ClientAbortException) { + /** + * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常, + * TODO 暂时去除异常处理。后续使用其他代理框架修改测试 + */ + + }else { + logger.error("录像服务 代理失败: ", e); + } + } catch (RuntimeException exception){ + logger.error("录像服务 代理失败: ", e); + } + } + + /** + * 对于为按照格式请求的可以直接返回404 + */ + @Override + protected String getTargetUri(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + + String uri = null; + if (mediaInfo != null) { +// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length()); + uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getRecordAssistPort()); + }else { + uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以 + } + return uri; + } + + /** + * 动态替换请求目标 + */ + @Override + protected HttpHost getTargetHost(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + HttpHost host; + if (mediaInfo != null) { + host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort()); + }else { + host = new HttpHost("127.0.0.1", serverPort); + } + return host; + + } + + /** + * 根据uri获取流媒体信息 + */ + MediaServerItem getMediaInfoByUri(String uri){ + String[] split = uri.split("/"); + String mediaServerId = split[2]; + if ("default".equalsIgnoreCase(mediaServerId)) { + return mediaServerService.getDefaultMediaServer(); + }else { + return mediaServerService.getOne(mediaServerId); + } + + } + + /** + * 去掉url中的标志信息 + */ + @Override + protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) { + String requestURI = servletRequest.getRequestURI(); + MediaServerItem mediaInfo = getMediaInfoByUri(requestURI); + String url = super.rewriteUrlFromRequest(servletRequest); + if (mediaInfo == null) { + logger.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); + return url; + } + if (!ObjectUtils.isEmpty(mediaInfo.getId())) { + url = url.replace(mediaInfo.getId() + "/", ""); + } + return url.replace("default/", ""); + } + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/ScheduleConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/ScheduleConfig.java new file mode 100644 index 0000000..d6efcfa --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/ScheduleConfig.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.conf; + +import org.springframework.boot.autoconfigure.batch.BatchProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.annotation.SchedulingConfigurer; +import org.springframework.scheduling.config.ScheduledTaskRegistrar; + +import java.lang.reflect.Method; +import java.util.concurrent.Executors; + +@Configuration +public class ScheduleConfig implements SchedulingConfigurer { + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + Method[] methods = BatchProperties.Job.class.getMethods(); + int defaultPoolSize = 20; + int corePoolSize = 0; + if (methods != null && methods.length > 0) { + for (Method method : methods) { + Scheduled annotation = method.getAnnotation(Scheduled.class); + if (annotation != null) { + corePoolSize++; + } + } + if (defaultPoolSize > corePoolSize){ + corePoolSize = defaultPoolSize; + } + + } + taskRegistrar.setScheduler(Executors.newScheduledThreadPool(corePoolSize)); + + } +} \ No newline at end of file diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/ServiceInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/ServiceInfo.java new file mode 100644 index 0000000..e761b8d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/ServiceInfo.java @@ -0,0 +1,30 @@ +package com.yfd.monitor.conf; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Component +public class ServiceInfo implements ApplicationListener { + + private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class); + + private static int serverPort; + + public static int getServerPort() { + return serverPort; + } + + @Override + public void onApplicationEvent(WebServerInitializedEvent event) { + // 项目启动获取启动的端口号 + ServiceInfo.serverPort = event.getWebServer().getPort(); + logger.info("项目启动获取启动的端口号: " + ServiceInfo.serverPort); + } + + public void setServerPort(int serverPort) { + ServiceInfo.serverPort = serverPort; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/SipConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/SipConfig.java new file mode 100644 index 0000000..43a07f8 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/SipConfig.java @@ -0,0 +1,110 @@ +package com.yfd.monitor.conf; + + +import org.junit.jupiter.api.Order; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true) +@Order(0) +public class SipConfig { + + private String ip; + + private String showIp; + + private Integer port; + + private String domain; + + private String id; + + private String password; + + Integer ptzSpeed = 50; + + Integer registerTimeInterval = 120; + + private boolean alarm; + + public void setIp(String ip) { + this.ip = ip; + } + + public void setPort(Integer port) { + this.port = port; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setId(String id) { + this.id = id; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setPtzSpeed(Integer ptzSpeed) { + this.ptzSpeed = ptzSpeed; + } + + + public void setRegisterTimeInterval(Integer registerTimeInterval) { + this.registerTimeInterval = registerTimeInterval; + } + + public String getIp() { + return ip; + } + + + public Integer getPort() { + return port; + } + + + public String getDomain() { + return domain; + } + + + public String getId() { + return id; + } + + public String getPassword() { + return password; + } + + + public Integer getPtzSpeed() { + return ptzSpeed; + } + + public Integer getRegisterTimeInterval() { + return registerTimeInterval; + } + + public boolean isAlarm() { + return alarm; + } + + public void setAlarm(boolean alarm) { + this.alarm = alarm; + } + + public String getShowIp() { + if (this.showIp == null) { + return this.ip; + } + return showIp; + } + + public void setShowIp(String showIp) { + this.showIp = showIp; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/SipPlatformRunner.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/SipPlatformRunner.java new file mode 100644 index 0000000..5ac0316 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/SipPlatformRunner.java @@ -0,0 +1,56 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.ParentPlatformCatch; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.service.IPlatformService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 系统启动时控制上级平台重新注册 + */ +@Component +@Order(value=13) +public class SipPlatformRunner implements CommandLineRunner { + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IPlatformService platformService; + + @Autowired + private ISIPCommanderForPlatform sipCommanderForPlatform; + + + @Override + public void run(String... args) throws Exception { + // 获取所有启用的平台 + List parentPlatforms = storager.queryEnableParentPlatformList(true); + for (ParentPlatform parentPlatform : parentPlatforms) { + + ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + + // 更新缓存 + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + if (parentPlatformCatchOld != null) { + platformService.offline(parentPlatform,true); + } + platformService.login(parentPlatform); + + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/SpringDocConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/SpringDocConfig.java new file mode 100644 index 0000000..f00af64 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/SpringDocConfig.java @@ -0,0 +1,85 @@ +package com.yfd.monitor.conf; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.junit.jupiter.api.Order; +import org.springdoc.core.GroupedOpenApi; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +@Order(1) +public class SpringDocConfig { + + @Value("${doc.enabled: true}") + private boolean enable; + + @Bean + public OpenAPI springShopOpenApi() { + Contact contact = new Contact(); + contact.setName("pan"); + contact.setEmail("648540858@qq.com"); + return new OpenAPI() + .info(new Info().title("MonitorServer 接口文档") + .contact(contact) + .description("开箱即用的28181协议视频平台") + .version("v2.0") + .license(new License().name("Apache 2.0").url("http://springdoc.org"))); + } + + /** + * 添加分组 + * @return + */ + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("1. 全部") + .packagesToScan("com.yfd.monitor.vmanager") + .build(); + } + + @Bean + public GroupedOpenApi publicApi2() { + return GroupedOpenApi.builder() + .group("2. 国标28181") + .packagesToScan("com.yfd.monitor.vmanager.gdw2019") + .build(); + } + + @Bean + public GroupedOpenApi publicApi3() { + return GroupedOpenApi.builder() + .group("3. 拉流转发") + .packagesToScan("com.yfd.monitor.vmanager.streamProxy") + .build(); + } + + @Bean + public GroupedOpenApi publicApi4() { + return GroupedOpenApi.builder() + .group("4. 推流管理") + .packagesToScan("com.yfd.monitor.vmanager.streamPush") + .build(); + } + + @Bean + public GroupedOpenApi publicApi5() { + return GroupedOpenApi.builder() + .group("4. 服务管理") + .packagesToScan("com.yfd.monitor.vmanager.server") + .build(); + } + + @Bean + public GroupedOpenApi publicApi6() { + return GroupedOpenApi.builder() + .group("5. 用户管理") + .packagesToScan("com.yfd.monitor.vmanager.user") + .build(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/SystemInfoTimerTask.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/SystemInfoTimerTask.java new file mode 100644 index 0000000..3ecb638 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/SystemInfoTimerTask.java @@ -0,0 +1,42 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd.AlarmQueryMessageHandler; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.utils.SystemInfoUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * 获取系统信息写入redis + */ +@Component +public class SystemInfoTimerTask { + + private Logger logger = LoggerFactory.getLogger(SystemInfoTimerTask.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + +// @Scheduled(fixedRate = 2000) //每1秒执行一次 + public void execute(){ + try { + double cpuInfo = SystemInfoUtils.getCpuInfo(); + redisCatchStorage.addCpuInfo(cpuInfo); + double memInfo = SystemInfoUtils.getMemInfo(); + redisCatchStorage.addMemInfo(memInfo); + Map networkInterfaces = SystemInfoUtils.getNetworkInterfaces(); + redisCatchStorage.addNetInfo(networkInterfaces); + List> diskInfo =SystemInfoUtils.getDiskInfo(); + redisCatchStorage.addDiskInfo(diskInfo); + } catch (InterruptedException e) { + logger.error("[获取系统信息失败] {}", e.getMessage()); + } + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/ThreadPoolTaskConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/ThreadPoolTaskConfig.java new file mode 100644 index 0000000..29fd4c0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/ThreadPoolTaskConfig.java @@ -0,0 +1,69 @@ +package com.yfd.monitor.conf; + +import org.junit.jupiter.api.Order; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * ThreadPoolTask 配置类 + */ +@Configuration +@Order(1) +@EnableAsync(proxyTargetClass = true) +public class ThreadPoolTaskConfig { + + public static final int cpuNum = Runtime.getRuntime().availableProcessors(); + + /** + * 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务, + * 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中; + * 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝 + */ + + /** + * 核心线程数(默认线程数) + */ + private static final int corePoolSize = cpuNum; + /** + * 最大线程数 + */ + private static final int maxPoolSize = cpuNum*2; + /** + * 允许线程空闲时间(单位:默认为秒) + */ + private static final int keepAliveTime = 30; + + /** + * 缓冲队列大小 + */ + private static final int queueCapacity = 10000; + /** + * 线程池名前缀 + */ + private static final String threadNamePrefix = "wvp-"; + + /** + * + * @return + */ + @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名 + public ThreadPoolTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveTime); + executor.setThreadNamePrefix(threadNamePrefix); + + // 线程池对拒绝任务的处理策略 + // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + // 初始化 + executor.initialize(); + return executor; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/UserSetting.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/UserSetting.java new file mode 100644 index 0000000..555c2e0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/UserSetting.java @@ -0,0 +1,330 @@ +package com.yfd.monitor.conf; + +import org.junit.jupiter.api.Order; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 配置文件 user-settings 映射的配置信息 + */ +@Component +@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) +@Order(0) +public class UserSetting { + + private Boolean savePositionHistory = Boolean.FALSE; + + private Boolean autoApplyPlay = Boolean.FALSE; + + private Boolean seniorSdp = Boolean.FALSE; + + private Integer playTimeout = 18000; + + private int platformPlayTimeout = 60000; + + private Boolean interfaceAuthentication = Boolean.TRUE; + + private Boolean recordPushLive = Boolean.TRUE; + + private Boolean recordSip = Boolean.TRUE; + + private Boolean logInDatebase = Boolean.TRUE; + + private Boolean usePushingAsStatus = Boolean.TRUE; + + private Boolean useSourceIpAsStreamIp = Boolean.FALSE; + + private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE; + + private Boolean streamOnDemand = Boolean.TRUE; + + private Boolean pushAuthority = Boolean.TRUE; + + private Boolean gbSendStreamStrict = Boolean.FALSE; + + private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; + + private Boolean sipLog = Boolean.FALSE; + private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE; + + private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE; + + private Boolean deviceStatusNotify = Boolean.FALSE; + + private String serverId = "000000"; + + private String recordPath = null; + + private String thirdPartyGBIdReg = "[\\s\\S]*"; + + private List interfaceAuthenticationExcludes = new ArrayList<>(); + + private List allowedOrigins = new ArrayList<>(); + + + + private int maxNotifyCountQueue = 10000; + + private int registerAgainAfterTime = 60; + + private boolean registerKeepIntDialog = false; + + private String snapfilepath = null; + + private String ffmpegpath = null; + + public Boolean getSavePositionHistory() { + return savePositionHistory; + } + + public Boolean isSavePositionHistory() { + return savePositionHistory; + } + + public Boolean isAutoApplyPlay() { + return autoApplyPlay; + } + + public Boolean isSeniorSdp() { + return seniorSdp; + } + + public Integer getPlayTimeout() { + return playTimeout; + } + + public Boolean isInterfaceAuthentication() { + return interfaceAuthentication; + } + + public Boolean isRecordPushLive() { + return recordPushLive; + } + + public List getInterfaceAuthenticationExcludes() { + return interfaceAuthenticationExcludes; + } + + public void setSavePositionHistory(Boolean savePositionHistory) { + this.savePositionHistory = savePositionHistory; + } + + public void setAutoApplyPlay(Boolean autoApplyPlay) { + this.autoApplyPlay = autoApplyPlay; + } + + public void setSeniorSdp(Boolean seniorSdp) { + this.seniorSdp = seniorSdp; + } + + public void setPlayTimeout(Integer playTimeout) { + this.playTimeout = playTimeout; + } + + private Boolean useCustomSsrcForParentInvite = Boolean.TRUE; + + public void setInterfaceAuthentication(boolean interfaceAuthentication) { + this.interfaceAuthentication = interfaceAuthentication; + } + + public void setRecordPushLive(Boolean recordPushLive) { + this.recordPushLive = recordPushLive; + } + + public void setInterfaceAuthenticationExcludes(List interfaceAuthenticationExcludes) { + this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes; + } + public Boolean getUseCustomSsrcForParentInvite() { + return useCustomSsrcForParentInvite; + } + + public void setUseCustomSsrcForParentInvite(Boolean useCustomSsrcForParentInvite) { + this.useCustomSsrcForParentInvite = useCustomSsrcForParentInvite; + } + public Boolean getLogInDatebase() { + return logInDatebase; + } + + public void setLogInDatebase(Boolean logInDatebase) { + this.logInDatebase = logInDatebase; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public String getThirdPartyGBIdReg() { + return thirdPartyGBIdReg; + } + + public void setThirdPartyGBIdReg(String thirdPartyGBIdReg) { + this.thirdPartyGBIdReg = thirdPartyGBIdReg; + } + + public Boolean getRecordSip() { + return recordSip; + } + + public void setRecordSip(Boolean recordSip) { + this.recordSip = recordSip; + } + + public int getPlatformPlayTimeout() { + return platformPlayTimeout; + } + + public void setPlatformPlayTimeout(int platformPlayTimeout) { + this.platformPlayTimeout = platformPlayTimeout; + } + + public Boolean isUsePushingAsStatus() { + return usePushingAsStatus; + } + + public void setUsePushingAsStatus(Boolean usePushingAsStatus) { + this.usePushingAsStatus = usePushingAsStatus; + } + + public Boolean getStreamOnDemand() { + return streamOnDemand; + } + + public void setStreamOnDemand(Boolean streamOnDemand) { + this.streamOnDemand = streamOnDemand; + } + + public Boolean getUseSourceIpAsStreamIp() { + return useSourceIpAsStreamIp; + } + + public void setUseSourceIpAsStreamIp(Boolean useSourceIpAsStreamIp) { + this.useSourceIpAsStreamIp = useSourceIpAsStreamIp; + } + + public Boolean getPushAuthority() { + return pushAuthority; + } + + public void setPushAuthority(Boolean pushAuthority) { + this.pushAuthority = pushAuthority; + } + + public Boolean getGbSendStreamStrict() { + return gbSendStreamStrict; + } + + public void setGbSendStreamStrict(Boolean gbSendStreamStrict) { + this.gbSendStreamStrict = gbSendStreamStrict; + } + + public Boolean getSyncChannelOnDeviceOnline() { + return syncChannelOnDeviceOnline; + } + + public void setSyncChannelOnDeviceOnline(Boolean syncChannelOnDeviceOnline) { + this.syncChannelOnDeviceOnline = syncChannelOnDeviceOnline; + } + + public Boolean getSipUseSourceIpAsRemoteAddress() { + return sipUseSourceIpAsRemoteAddress; + } + + public void setSipUseSourceIpAsRemoteAddress(Boolean sipUseSourceIpAsRemoteAddress) { + this.sipUseSourceIpAsRemoteAddress = sipUseSourceIpAsRemoteAddress; + } + + public Boolean getSipLog() { + return sipLog; + } + + public void setSipLog(Boolean sipLog) { + this.sipLog = sipLog; + } + + public List getAllowedOrigins() { + return allowedOrigins; + } + + public void setAllowedOrigins(List allowedOrigins) { + this.allowedOrigins = allowedOrigins; + } + + public Boolean getSendToPlatformsWhenIdLost() { + return sendToPlatformsWhenIdLost; + } + + public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) { + this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost; + } + + public Boolean getRefuseChannelStatusChannelFormNotify() { + return refuseChannelStatusChannelFormNotify; + } + + public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) { + this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify; + } + + public String getRecordPath() { + return recordPath; + } + + public void setRecordPath(String recordPath) { + this.recordPath = recordPath; + } + + public int getMaxNotifyCountQueue() { + return maxNotifyCountQueue; + } + + public void setMaxNotifyCountQueue(int maxNotifyCountQueue) { + this.maxNotifyCountQueue = maxNotifyCountQueue; + } + + public Boolean getDeviceStatusNotify() { + return deviceStatusNotify; + } + + public void setDeviceStatusNotify(Boolean deviceStatusNotify) { + this.deviceStatusNotify = deviceStatusNotify; + } + + public int getRegisterAgainAfterTime() { + return registerAgainAfterTime; + } + + public void setRegisterAgainAfterTime(int registerAgainAfterTime) { + this.registerAgainAfterTime = registerAgainAfterTime; + } + + public boolean isRegisterKeepIntDialog() { + return registerKeepIntDialog; + } + + public void setRegisterKeepIntDialog(boolean registerKeepIntDialog) { + this.registerKeepIntDialog = registerKeepIntDialog; + } + public String getSnapfilepath() { + return snapfilepath; + } + + public void setSnapfilepath(String snapfilepath) { + this.snapfilepath = snapfilepath; + } + + public String getFfmpegpath() { + return ffmpegpath; + } + + public void setFfmpegpath(String ffmpegpath) { + this.ffmpegpath = ffmpegpath; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/VersionConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/VersionConfig.java new file mode 100644 index 0000000..6e1bf99 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/VersionConfig.java @@ -0,0 +1,39 @@ +package com.yfd.monitor.conf; + +import org.junit.jupiter.api.Order; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "version") +@Order(0) +public class VersionConfig { + + private String version; + private String artifactId; + private String description; + + public void setVersion(String version) { + this.version = version; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getVersion() { + return version; + } + + public String getArtifactId() { + return artifactId; + } + + public String getDescription() { + return description; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/VersionInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/VersionInfo.java new file mode 100644 index 0000000..79894c8 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/VersionInfo.java @@ -0,0 +1,26 @@ +package com.yfd.monitor.conf; + +import com.yfd.monitor.common.VersionPo; +import com.yfd.monitor.utils.GitUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class VersionInfo { + + @Autowired + GitUtil gitUtil; + + public VersionPo getVersion() { + VersionPo versionPo = new VersionPo(); + versionPo.setGIT_Revision(gitUtil.getGitCommitId()); + versionPo.setGIT_BRANCH(gitUtil.getBranch()); + versionPo.setGIT_URL(gitUtil.getGitUrl()); + versionPo.setBUILD_DATE(gitUtil.getBuildDate()); + versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort()); + versionPo.setVersion(gitUtil.getBuildVersion()); + versionPo.setGIT_DATE(gitUtil.getCommitTime()); + + return versionPo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/WVPTimerTask.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/WVPTimerTask.java new file mode 100644 index 0000000..97b8999 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/WVPTimerTask.java @@ -0,0 +1,33 @@ +package com.yfd.monitor.conf; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class WVPTimerTask { + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaServerService mediaServerService; + + @Value("${server.port}") + private int serverPort; + + @Autowired + private SipConfig sipConfig; + +// @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 + public void execute(){ + JSONObject jsonObject = new JSONObject(); + jsonObject.put("ip", sipConfig.getIp()); + jsonObject.put("port", serverPort); + redisCatchStorage.updateWVPInfo(jsonObject, 3); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/WebSocketServer.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/WebSocketServer.java new file mode 100644 index 0000000..35ed436 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/WebSocketServer.java @@ -0,0 +1,123 @@ +package com.yfd.monitor.conf; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.CopyOnWriteArrayList; + +@ServerEndpoint("/websocket/{token}") +@Component +@Slf4j +public class WebSocketServer { + private static int onlineCount=0;//在线人数 + private static CopyOnWriteArrayList webSocketSet=new CopyOnWriteArrayList();//在线用户集合 + private Session session;//与某个客户端的连接会话 + private String currentUser; + + @OnOpen + public void onOpen(@PathParam("token") String token, Session session){ + this.currentUser = token; + this.session=session; + webSocketSet.add(this);//加入set中 + addOnlineCount(); + log.info("有新连接加入!当前在线人数为"+getOnlineCount()); + allCurrentOnline(); + } + + @OnClose + public void onClose(){ + webSocketSet.remove(this); + subOnlineCount(); + log.info("有一连接关闭!当前在线人数为" + getOnlineCount()); + allCurrentOnline(); + } + + @OnMessage + public void onMessage(String message, Session session){ + log.info("来自客户端的消息:"+message); + for (WebSocketServer item:webSocketSet){ + try { + item.sendMessage(message); + } catch (IOException e) { + log.error("发生错误", e); + continue; + } + } + } + + @OnError + public void onError(Session session, Throwable throwable){ + log.info("发生错误!"); + } + + public void sendMessage(String message) throws IOException { + this.session.getBasicRemote().sendText(message); + } + + /** + * 获取当前所有在线用户名 + */ + public static void allCurrentOnline(){ + for (WebSocketServer item : webSocketSet) { + log.info(item.currentUser); + } + } + + /** + * 发送给指定用户 + */ + public static void sendMessageTo(String message,String token) throws IOException { + for (WebSocketServer item : webSocketSet) { + if(item.currentUser.equals(token)){ + item.session.getBasicRemote().sendText(message); + } + } + } + + /** + * 群发自定义消息 + */ + public static void sendInfo(String message) throws IOException { + for (WebSocketServer item : webSocketSet) { + try { + item.sendMessage(message); + } catch (IOException e) { + continue; + } + } + } + + /** + * 群发自定义消息 + */ + public static void sendInfo(String token,String message) { + System.out.println(message); + for (WebSocketServer item : webSocketSet) { + try { + if(item.currentUser.startsWith(token)){ + item.sendMessage(message); + } + + } catch (IOException e) { + continue; + } + } + } + + public static synchronized int getOnlineCount(){ + return onlineCount; + } + public static synchronized void addOnlineCount(){ + WebSocketServer.onlineCount++; + } + public static synchronized void subOnlineCount(){ + WebSocketServer.onlineCount--; + } + +} + + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/ControllerException.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/ControllerException.java new file mode 100644 index 0000000..38b573c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/ControllerException.java @@ -0,0 +1,37 @@ +package com.yfd.monitor.conf.exception; + +import com.yfd.monitor.vmanager.bean.ErrorCode; + +/** + * 自定义异常,controller出现错误时直接抛出异常由全局异常捕获并返回结果 + */ +public class ControllerException extends RuntimeException{ + + private int code; + private String msg; + + public ControllerException(int code, String msg) { + this.code = code; + this.msg = msg; + } + public ControllerException(ErrorCode errorCode) { + this.code = errorCode.getCode(); + this.msg = errorCode.getMsg(); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/ServiceException.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/ServiceException.java new file mode 100644 index 0000000..ef5a2df --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/ServiceException.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.conf.exception; + + +public class ServiceException extends Exception{ + private String msg; + + + + public ServiceException(String msg) { + this.msg = msg; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + @Override + public String getMessage() { + return msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/SsrcTransactionNotFoundException.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/SsrcTransactionNotFoundException.java new file mode 100644 index 0000000..3bc6b2d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/exception/SsrcTransactionNotFoundException.java @@ -0,0 +1,47 @@ +package com.yfd.monitor.conf.exception; + + +public class SsrcTransactionNotFoundException extends Exception{ + private String deviceId; + private String channelId; + private String callId; + private String stream; + + + + public SsrcTransactionNotFoundException(String deviceId, String channelId, String callId, String stream) { + this.deviceId = deviceId; + this.channelId = channelId; + this.callId = callId; + this.stream = stream; + } + + public String getDeviceId() { + return deviceId; + } + + public String getChannelId() { + return channelId; + } + + public String getCallId() { + return callId; + } + + public String getStream() { + return stream; + } + + @Override + public String getMessage() { + StringBuffer msg = new StringBuffer(); + msg.append(String.format("缓存事务信息未找到,device:%s channel: %s ", deviceId, channelId)); + if (callId != null) { + msg.append(",callId: " + callId); + } + if (stream != null) { + msg.append(",stream: " + stream); + } + return msg.toString(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/redis/RedisMsgListenConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/redis/RedisMsgListenConfig.java new file mode 100644 index 0000000..ec350c0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/redis/RedisMsgListenConfig.java @@ -0,0 +1,67 @@ +package com.yfd.monitor.conf.redis; + + +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.service.redisMsg.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + + +/** + * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 + * @date: 2019年5月30日 上午10:58:25 + * + */ +@Configuration +@Order(value=1) +public class RedisMsgListenConfig { + + @Autowired + private RedisGpsMsgListener redisGPSMsgListener; + + @Autowired + private RedisAlarmMsgListener redisAlarmMsgListener; + + @Autowired + private RedisStreamMsgListener redisStreamMsgListener; + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + + @Autowired + private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener; + + @Autowired + private RedisPushStreamStatusListMsgListener redisPushStreamListMsgListener; + + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; + + + /** + * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 + * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 + * + * @param connectionFactory + * @return + */ + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { + + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS)); + container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE)); + container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH")); + container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY)); + container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE)); + container.addMessageListener(redisPushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE)); + container.addMessageListener(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); + return container; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/redis/RedisTemplateConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/redis/RedisTemplateConfig.java new file mode 100644 index 0000000..0196bae --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/redis/RedisTemplateConfig.java @@ -0,0 +1,28 @@ +package com.yfd.monitor.conf.redis; + +import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisTemplateConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + // 使用fastJson序列化 + GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer(); + // value值的序列化采用fastJsonRedisSerializer + redisTemplate.setValueSerializer(fastJsonRedisSerializer); + redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); + + // key的序列化采用StringRedisSerializer + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/AnonymousAuthenticationEntryPoint.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/AnonymousAuthenticationEntryPoint.java new file mode 100644 index 0000000..1f73b6d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/AnonymousAuthenticationEntryPoint.java @@ -0,0 +1,46 @@ +package com.yfd.monitor.conf.security; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.security.dto.JwtUser; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 处理匿名用户访问逻辑 +* + */ +@Component +@Slf4j +public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { + String jwt = request.getHeader(JwtUtils.getHeader()); + JwtUser jwtUser = JwtUtils.verifyToken(jwt); + String username = jwtUser.getUserName(); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() ); + SecurityContextHolder.getContext().setAuthentication(token); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("code", ErrorCode.ERROR401.getCode()); + jsonObject.put("msg", ErrorCode.ERROR401.getMsg()); + String logUri = "api/user/login"; + if (request.getRequestURI().contains(logUri)){ + jsonObject.put("msg", e.getMessage()); + } + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + try { + response.getWriter().print(jsonObject.toJSONString()); + } catch (IOException ioException) { + log.error("发生错误", ioException); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/DefaultUserDetailsServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/DefaultUserDetailsServiceImpl.java new file mode 100644 index 0000000..1805473 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/DefaultUserDetailsServiceImpl.java @@ -0,0 +1,51 @@ +package com.yfd.monitor.conf.security; + +import com.alibaba.excel.util.StringUtils; +import com.yfd.monitor.conf.security.dto.LoginUser; +import com.yfd.monitor.service.IUserService; +import com.yfd.monitor.storager.dao.dto.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 用户登录认证逻辑 + */ +@Component +public class DefaultUserDetailsServiceImpl implements UserDetailsService { + + private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class); + + @Autowired + private IUserService userService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + if (StringUtils.isBlank(username)) { + logger.info("登录用户:{} 不存在", username); + throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); + } + + // 查出密码 + User user = userService.getUserByUsername(username); + if (user == null) { + logger.info("登录用户:{} 不存在", username); + throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); + } + String password = SecurityUtils.encryptPassword(user.getPassword()); + user.setPassword(password); + return new LoginUser(user, LocalDateTime.now()); + } + + + + + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/JwtAuthenticationFilter.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..0a6790a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/JwtAuthenticationFilter.java @@ -0,0 +1,84 @@ +package com.yfd.monitor.conf.security; + +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.security.dto.JwtUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; + +/** + * jwt token 过滤器 + */ + +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + + @Autowired + private UserSetting userSetting; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + + // 忽略登录请求的token验证 + String requestURI = request.getRequestURI(); + if (requestURI.equalsIgnoreCase("/api/user/login")) { + chain.doFilter(request, response); + return; + } + if (!userSetting.isInterfaceAuthentication()) { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, + new ArrayList<>()); + SecurityContextHolder.getContext().setAuthentication(token); + chain.doFilter(request, response); + return; + } + String jwt = request.getHeader(JwtUtils.getHeader()); + // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的 + // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口 + if (StringUtils.isBlank(jwt)) { + jwt = request.getParameter(JwtUtils.getHeader()); + if (StringUtils.isBlank(jwt)) { + chain.doFilter(request, response); + return; + } + } + + JwtUser jwtUser = JwtUtils.verifyToken(jwt); + String username = jwtUser.getUserName(); + // TODO 处理各个状态 + switch (jwtUser.getStatus()){ + case EXPIRED: + response.setStatus(400); + chain.doFilter(request, response); + // 异常 + return; + case EXCEPTION: + // 过期 + response.setStatus(400); + chain.doFilter(request, response); + return; + case EXPIRING_SOON: + // 即将过期 +// return; + default: + } + + // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() ); + SecurityContextHolder.getContext().setAuthentication(token); + chain.doFilter(request, response); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/JwtUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/JwtUtils.java new file mode 100644 index 0000000..a76cf36 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/JwtUtils.java @@ -0,0 +1,138 @@ +package com.yfd.monitor.conf.security; + +import com.yfd.monitor.conf.security.dto.JwtUser; +import org.jose4j.json.JsonUtil; +import org.jose4j.jwk.RsaJsonWebKey; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.NumericDate; +import org.jose4j.jwt.consumer.ErrorCodes; +import org.jose4j.jwt.consumer.InvalidJwtException; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.PrivateKey; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +public class JwtUtils { + + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); + + private static final String HEADER = "access-token"; + private static final String AUDIENCE = "Audience"; + + private static final long EXPIRED_THRESHOLD = 10 * 60; + + private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae"; + private static final String privateKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\",\"d\":\"ed7U_k3rJ4yTk70JtRSIfjKGiEb67BO1TabcymnljKO7RU8nage84zZYuSu_XpQsHk6P1f0Gzxkicghm_Er-FrfVn2pp70Xu52z3yRd6BJUgWLDFk97ngScIyw5OiULKU9SrZk2frDpftNCSUcIgb50F8m0QAnBa_CdPsQKbuuhLv8V8tBAV7F_lAwvSBgu56wRo3hPz5dWH8YeXM7XBfQ9viFMNEKd21sP_j5C7ueUnXT66nBxe3ZJEU3iuMYM6D6dB_KW2GfZC6WmTgvGhhxJD0h7aYmfjkD99MDleB7SkpbvoODOqiQ5Epb7Nyh6kv5u4KUv2CJYtATLZkUeMkQ\",\"p\":\"uBUjWPWtlGksmOqsqCNWksfqJvMcnP_8TDYN7e4-WnHL4N-9HjRuPDnp6kHvCIEi9SEfxm7gNxlRcWegvNQr3IZCz7TnCTexXc5NOklB9OavWFla6u-s3Thn6Tz45-EUjpJr0VJMxhO-KxGmuTwUXBBp4vN6K2qV6rQNFmgkWzk\",\"q\":\"tW_i7cCec56bHkhITL_79dXHz_PLC_f7xlynmlZJGU_d6mqOKmLBNBbTMLnYW8uAFiFzWxDeDHh1o5uF0mSQR-Z1Fg35OftnpbWpy0Cbc2la5WgXQjOwtG1eLYIY2BD3-wQ1VYDBCvowr4FDi-sngxwLqvwmrJ0xjhi99O-Gzcs\",\"dp\":\"q1d5jE85Hz_6M-eTh_lEluEf0NtPEc-vvhw-QO4V-cecNpbrCBdTWBmr4dE3NdpFeJc5ZVFEv-SACyei1MBEh0ItI_pFZi4BmMfy2ELh8ptaMMkTOESYyVy8U7veDq9RnBcr5i1Nqr0rsBkA77-9T6gzdvycBZdzLYAkAmwzEvk\",\"dq\":\"q29A2K08Crs-jmp2Bi8Q_8QzvIX6wSBbwZ4ir24AO-5_HNP56IrPS0yV2GCB0pqCOGb6_Hz_koDvhtuYoqdqvMVAtMoXR3YJBUaVXPt65p4RyNmFwIPe31zHs_BNUTsXVRMw4c16mci03-Af1sEm4HdLfxAp6sfM3xr5wcnhcek\",\"qi\":\"rHPgVTyHUHuYzcxfouyBfb1XAY8nshwn0ddo81o1BccD4Z7zo5It6SefDHjxCAbcmbiCcXBSooLcY-NF5FMv3fg19UE21VyLQltHcVjRRp2tRs4OHcM8yaXIU2x6N6Z6BP2tOksHb9MOBY1wAQzFOAKg_G4Sxev6-_6ud6RISuc\"}"; + private static final String publicKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\"}"; + + /** + * token过期时间(分钟) + */ + public static final long expirationTime = 60*24; + + public static String createToken(String username, String password) { + try { + /** + * “iss” (issuer) 发行人 + * + * “sub” (subject) 主题 + * + * “aud” (audience) 接收方 用户 + * + * “exp” (expiration time) 到期时间 + * + * “nbf” (not before) 在此之前不可用 + * + * “iat” (issued at) jwt的签发时间 + */ + //Payload + JwtClaims claims = new JwtClaims(); + claims.setGeneratedJwtId(); + claims.setIssuedAtToNow(); + // 令牌将过期的时间 分钟 + claims.setExpirationTimeMinutesInTheFuture(expirationTime); + claims.setNotBeforeMinutesInThePast(0); + claims.setSubject("login"); + claims.setAudience(AUDIENCE); + //添加自定义参数,必须是字符串类型 + claims.setClaim("username", username); + claims.setClaim("password", password); + + //jws + JsonWebSignature jws = new JsonWebSignature(); + //签名算法RS256 + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); + jws.setKeyIdHeaderValue(keyId); + jws.setPayload(claims.toJson()); + + PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey(); + jws.setKey(privateKey); + + //get token + String idToken = jws.getCompactSerialization(); + return idToken; + } catch (JoseException e) { + logger.error("[Token生成失败]: {}", e.getMessage()); + } + + return null; + } + + public static String getHeader() { + return HEADER; + } + + + public static JwtUser verifyToken(String token) { + + JwtUser jwtUser = new JwtUser(); + + try { + JwtConsumer consumer = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setMaxFutureValidityInMinutes(5256000) + .setAllowedClockSkewInSeconds(30) + .setRequireSubject() + //.setExpectedIssuer("") + .setExpectedAudience(AUDIENCE) + .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey()) + .build(); + + JwtClaims claims = consumer.processToClaims(token); + NumericDate expirationTime = claims.getExpirationTime(); + // 判断是否即将过期, 默认剩余时间小于5分钟未即将过期 + // 剩余时间 (秒) + long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue(); + if (timeRemaining < 5 * 60) { + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON); + }else { + jwtUser.setStatus(JwtUser.TokenStatus.NORMAL); + } + + String username = (String) claims.getClaimValue("username"); + String password = (String) claims.getClaimValue("password"); + jwtUser.setUserName(username); + jwtUser.setPassword(password); + + return jwtUser; + } catch (InvalidJwtException e) { + if (e.hasErrorCode(ErrorCodes.EXPIRED)) { + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); + }else { + jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION); + } + return jwtUser; + }catch (Exception e) { + logger.error("[Token解析失败]: {}", e.getMessage()); + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED); + return jwtUser; + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LoginFailureHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LoginFailureHandler.java new file mode 100644 index 0000000..bfac226 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LoginFailureHandler.java @@ -0,0 +1,65 @@ +package com.yfd.monitor.conf.security; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.*; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class LoginFailureHandler implements AuthenticationFailureHandler { + + private final static Logger logger = LoggerFactory.getLogger(LoginFailureHandler.class); + + @Autowired + private ObjectMapper objectMapper; + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { + + String username = request.getParameter("username"); + if (e instanceof AccountExpiredException) { + // 账号过期 + logger.info("[登录失败] - 用户[{}]账号过期", username); + + } else if (e instanceof BadCredentialsException) { + // 密码错误 + logger.info("[登录失败] - 用户[{}]密码/SIP服务器ID 错误", username); + + } else if (e instanceof CredentialsExpiredException) { + // 密码过期 + logger.info("[登录失败] - 用户[{}]密码过期", username); + + } else if (e instanceof DisabledException) { + // 用户被禁用 + logger.info("[登录失败] - 用户[{}]被禁用", username); + + } else if (e instanceof LockedException) { + // 用户被锁定 + logger.info("[登录失败] - 用户[{}]被锁定", username); + + } else if (e instanceof InternalAuthenticationServiceException) { + // 内部错误 + logger.error(String.format("[登录失败] - [%s]内部错误", username), e); + + } else { + // 其他错误 + logger.error(String.format("[登录失败] - [%s]其他错误", username), e); + } + Map map = new HashMap<>(); + map.put("code","0"); + map.put("msg","登录失败"); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(objectMapper.writeValueAsString(map)); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LoginSuccessHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LoginSuccessHandler.java new file mode 100644 index 0000000..8c53545 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LoginSuccessHandler.java @@ -0,0 +1,34 @@ +package com.yfd.monitor.conf.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@Component +public class LoginSuccessHandler implements AuthenticationSuccessHandler { + + private final static Logger logger = LoggerFactory.getLogger(LoginSuccessHandler.class); + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { +// String username = request.getParameter("username"); +// httpServletResponse.setContentType("application/json;charset=UTF-8"); +// // 生成JWT,并放置到请求头中 +// String jwt = JwtUtils.createToken(authentication.getName(), ); +// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt); +// ServletOutputStream outputStream = httpServletResponse.getOutputStream(); +// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8)); +// outputStream.flush(); +// outputStream.close(); + +// logger.info("[登录成功] - [{}]", username); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LogoutHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LogoutHandler.java new file mode 100644 index 0000000..0de3e9a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/LogoutHandler.java @@ -0,0 +1,27 @@ +package com.yfd.monitor.conf.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 退出登录成功 + */ +@Component +public class LogoutHandler implements LogoutSuccessHandler { + + private final static Logger logger = LoggerFactory.getLogger(LogoutHandler.class); + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { + String username = request.getParameter("username"); + logger.info("[退出登录成功] - [{}]", username); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/SecurityUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/SecurityUtils.java new file mode 100644 index 0000000..0cc8738 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/SecurityUtils.java @@ -0,0 +1,88 @@ +package com.yfd.monitor.conf.security; + +import com.yfd.monitor.conf.security.dto.LoginUser; +import com.yfd.monitor.storager.dao.dto.User; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import javax.security.sasl.AuthenticationException; +import java.time.LocalDateTime; + +public class SecurityUtils { + + /** + * 描述根据账号密码进行调用security进行认证授权 主动调 + * 用AuthenticationManager的authenticate方法实现 + * 授权成功后将用户信息存入SecurityContext当中 + * @param username 用户名 + * @param password 密码 + * @param authenticationManager 认证授权管理器, + * @see AuthenticationManager + * @return UserInfo 用户信息 + */ + public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException { + //使用security框架自带的验证token生成器 也可以自定义。 + UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password); + //认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功 + Authentication authenticate = authenticationManager.authenticate(token); + LoginUser user = (LoginUser) authenticate.getPrincipal(); + + SecurityContextHolder.getContext().setAuthentication(token); + + return user; + } + + /** + * 获取当前登录的所有认证信息 + * @return + */ + public static Authentication getAuthentication(){ + SecurityContext context = SecurityContextHolder.getContext(); + return context.getAuthentication(); + } + + /** + * 获取当前登录用户信息 + * @return + */ + public static LoginUser getUserInfo(){ + Authentication authentication = getAuthentication(); + if(authentication!=null){ + Object principal = authentication.getPrincipal(); + if(principal!=null && !"anonymousUser".equals(principal)){ +// LoginUser user = (LoginUser) authentication.getPrincipal(); + + String username = (String) principal; + User user = new User(); + user.setUsername(username); + LoginUser loginUser = new LoginUser(user, LocalDateTime.now()); + return loginUser; + } + } + return null; + } + + /** + * 获取当前登录用户ID + * @return + */ + public static int getUserId(){ + LoginUser user = getUserInfo(); + return user.getId(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/WebSecurityConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/WebSecurityConfig.java new file mode 100644 index 0000000..82eecb7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/WebSecurityConfig.java @@ -0,0 +1,169 @@ +package com.yfd.monitor.conf.security; + +import com.yfd.monitor.conf.UserSetting; +import org.junit.jupiter.api.Order; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.CorsUtils; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * 配置Spring Security +* + */ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@Order(1) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private DefaultUserDetailsServiceImpl userDetailsService; + /** + * 登出成功的处理 + */ + @Autowired + private LoginFailureHandler loginFailureHandler; + /** + * 登录成功的处理 + */ + @Autowired + private LoginSuccessHandler loginSuccessHandler; + /** + * 登出成功的处理 + */ + @Autowired + private LogoutHandler logoutHandler; + /** + * 未登录的处理 + */ + @Autowired + private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint; + @Autowired + private JwtAuthenticationFilter jwtAuthenticationFilter; + + + /** + * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链 + **/ + @Override + public void configure(WebSecurity web) { + if (userSetting.isInterfaceAuthentication()) { + ArrayList matchers = new ArrayList<>(); + matchers.add("/"); + matchers.add("/#/**"); + matchers.add("/static/**"); + matchers.add("/index.html"); + matchers.add("/doc.html"); + matchers.add("/webjars/**"); + matchers.add("/swagger-resources/**"); + matchers.add("/v3/api-docs/**"); + matchers.add("/js/**"); + matchers.add("/api/device/query/snap/**"); + matchers.add("/record_proxy/*/**"); + matchers.addAll(userSetting.getInterfaceAuthenticationExcludes()); + // 可以直接访问的静态数据 + web.ignoring().antMatchers(matchers.toArray(new String[0])); + } + } + + /** + * 配置认证方式 + * @param auth + * @throws Exception + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + // 设置不隐藏 未找到用户异常 + provider.setHideUserNotFoundExceptions(true); + // 用户认证service - 查询数据库的逻辑 + provider.setUserDetailsService(userDetailsService); + // 设置密码加密算法 + provider.setPasswordEncoder(passwordEncoder()); + auth.authenticationProvider(provider); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.headers().contentTypeOptions().disable() + .and().cors().configurationSource(configurationSource()) + .and().csrf().disable() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + + + // 配置拦截规则 + .and() + .authorizeRequests() + .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() + .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll() + .antMatchers("/api/user/login","/**","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll() + .anyRequest().authenticated() + // 异常处理器 + .and() + .exceptionHandling() + .authenticationEntryPoint(anonymousAuthenticationEntryPoint) + .and().logout().logoutUrl("/api/user/logout").permitAll() + .logoutSuccessHandler(logoutHandler) + ; + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + } + + CorsConfigurationSource configurationSource(){ + // 配置跨域 + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.setAllowedHeaders(Arrays.asList("*")); + corsConfiguration.setAllowedMethods(Arrays.asList("*")); + corsConfiguration.setMaxAge(3600L); + corsConfiguration.setAllowCredentials(true); + corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins()); + corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader())); + + UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource(); + url.registerCorsConfiguration("/**",corsConfiguration); + return url; + } + + /** + * 描述: 密码加密算法 BCrypt 推荐使用 + **/ + @Bean + public BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * 描述: 注入AuthenticationManager管理器 + **/ + @Override + @Bean + public AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/dto/JwtUser.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/dto/JwtUser.java new file mode 100644 index 0000000..a52fdd1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/dto/JwtUser.java @@ -0,0 +1,53 @@ +package com.yfd.monitor.conf.security.dto; + +public class JwtUser { + + public enum TokenStatus{ + /** + * 正常的使用状态 + */ + NORMAL, + /** + * 过期而失效 + */ + EXPIRED, + /** + * 即将过期 + */ + EXPIRING_SOON, + /** + * 异常 + */ + EXCEPTION + } + + private String userName; + + private String password; + + private TokenStatus status; + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public TokenStatus getStatus() { + return status; + } + + public void setStatus(TokenStatus status) { + this.status = status; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/conf/security/dto/LoginUser.java b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/dto/LoginUser.java new file mode 100644 index 0000000..082d1c4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/conf/security/dto/LoginUser.java @@ -0,0 +1,111 @@ +package com.yfd.monitor.conf.security.dto; + +import com.yfd.monitor.storager.dao.dto.Role; +import com.yfd.monitor.storager.dao.dto.User; +import org.springframework.security.core.CredentialsContainer; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; +import org.springframework.security.core.userdetails.UserDetails; + +import java.time.LocalDateTime; +import java.util.Collection; + +public class LoginUser implements UserDetails, CredentialsContainer { + + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + /** + * 用户 + */ + private User user; + + private String accessToken; + + + /** + * 登录时间 + */ + private LocalDateTime loginTime; + + public LoginUser(User user, LocalDateTime loginTime) { + this.user = user; + this.loginTime = loginTime; + } + + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @Override + public boolean isAccountNonExpired() { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + *

+ * 密码锁定 + *

+ */ + @Override + public boolean isAccountNonLocked() { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + */ + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + /** + * 用户是否被启用或禁用。禁用的用户无法进行身份验证。 + */ + @Override + public boolean isEnabled() { + return true; + } + + /** + * 认证完成后,擦除密码 + */ + @Override + public void eraseCredentials() { + user.setPassword(null); + } + + + public int getId() { + return user.getId(); + } + + public Role getRole() { + return user.getRole(); + } + + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/SipLayer.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/SipLayer.java new file mode 100644 index 0000000..9da7f8d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/SipLayer.java @@ -0,0 +1,202 @@ +package com.yfd.monitor.gdw2019; + +import cn.hutool.core.util.StrUtil; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.conf.DefaultProperties; +import com.yfd.monitor.gdw2019.transmit.ISIPProcessorObserver; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.SipProviderImpl; +import gov.nist.javax.sip.SipStackImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Component +@Order(value = 10) +public class SipLayer implements CommandLineRunner { + + private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); + + @Autowired + private SipConfig sipConfig; + + @Autowired + private ISIPProcessorObserver sipProcessorObserver; + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private UserSetting userSetting; + + private final Map tcpSipProviderMap = new ConcurrentHashMap<>(); + private final Map udpSipProviderMap = new ConcurrentHashMap<>(); + + @Override + public void run(String... args) { + List monitorIps = new ArrayList<>(); + // 使用逗号分割多个ip + String separator = ","; + if (sipConfig.getIp().indexOf(separator) > 0) { + String[] split = sipConfig.getIp().split(separator); + monitorIps.addAll(Arrays.asList(split)); + } else { + monitorIps.add(sipConfig.getIp()); + } + + SipFactory.getInstance().setPathName("gov.nist"); + if (monitorIps.size() > 0) { + for (String monitorIp : monitorIps) { + addListeningPoint(monitorIp, sipConfig.getPort()); + } + if (udpSipProviderMap.size() + tcpSipProviderMap.size() == 0) { + System.exit(1); + } + } + } + + + private void addListeningPoint(String monitorIp, int port) { + SipStackImpl sipStack; + try { + Properties properties = DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()); + sipStack = + (SipStackImpl) SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, + userSetting.getSipLog())); + } catch (PeerUnavailableException e) { + logger.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); + return; + } + List parentPlatforms = storager.queryEnableParentPlatformList(true); + String ip = ""; + if (parentPlatforms.size() > 0) { + ip = parentPlatforms.get(0).getDeviceIp(); + } + + try { + System.out.println("----------------------------------------------------------------------------"); + ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "TCP"); + System.out.println("----------------------------------------------------------------------------1"); + SipProviderImpl tcpSipProvider = (SipProviderImpl) sipStack.createSipProvider(tcpListeningPoint); + System.out.println("----------------------------------------------------------------------------2"); + tcpSipProvider.setDialogErrorsAutomaticallyHandled(); + System.out.println("----------------------------------------------------------------------------3"); + tcpSipProvider.addSipListener(sipProcessorObserver); + System.out.println("----------------------------------------------------------------------------4"); + System.out.println(monitorIp); + System.out.println(tcpSipProviderMap.toString()); + tcpSipProviderMap.put(monitorIp, tcpSipProvider); + logger.info("[Sip Server] tcp://{}:{} 启动成功", monitorIp, port); + try { + if (StrUtil.isNotBlank(ip) && !ip.equals(monitorIp)) { + ListeningPoint tcpListeningPoint1 = sipStack.createListeningPoint(ip, 5060, "TCP"); + SipProviderImpl tcpSipProvider1 = (SipProviderImpl) sipStack.createSipProvider(tcpListeningPoint1); + tcpSipProvider1.setDialogErrorsAutomaticallyHandled(); + tcpSipProvider1.addSipListener(sipProcessorObserver); + + tcpSipProviderMap.put(ip, tcpSipProvider1); + + + ListeningPoint tcpListeningPoint2 = sipStack.createListeningPoint(ip, 5083, "TCP"); + SipProviderImpl tcpSipProvider2 = (SipProviderImpl) sipStack.createSipProvider(tcpListeningPoint2); + tcpSipProvider2.setDialogErrorsAutomaticallyHandled(); + tcpSipProvider2.addSipListener(sipProcessorObserver); + tcpSipProviderMap.put(ip+"5083", tcpSipProvider2); + } + + }catch (Exception e){ + e.printStackTrace(); + logger.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + , ip, 5060); + } + + + } catch (TransportNotSupportedException + | TooManyListenersException + | ObjectInUseException + | InvalidArgumentException e) { + e.printStackTrace(); + logger.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + , monitorIp, port); + } + + try { + ListeningPoint udpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "UDP"); + SipProviderImpl udpSipProvider = (SipProviderImpl) sipStack.createSipProvider(udpListeningPoint); + udpSipProvider.addSipListener(sipProcessorObserver); + udpSipProviderMap.put(monitorIp, udpSipProvider); + try { + if (StrUtil.isNotBlank(ip) && !ip.equals(monitorIp)) { + ListeningPoint udpListeningPoint1 = sipStack.createListeningPoint(ip, 5060, "UDP"); + SipProviderImpl udpSipProvider1 = (SipProviderImpl) sipStack.createSipProvider(udpListeningPoint1); + udpSipProvider1.addSipListener(sipProcessorObserver); + udpSipProviderMap.put(ip, udpSipProvider1); + + + ListeningPoint udpListeningPoint2 = sipStack.createListeningPoint(ip, 5083, "UDP"); + SipProviderImpl udpSipProvider2 = (SipProviderImpl) sipStack.createSipProvider(udpListeningPoint2); + udpSipProvider2.addSipListener(sipProcessorObserver); + udpSipProviderMap.put(ip+"5083", udpSipProvider2); + } + }catch (Exception e){ + e.printStackTrace(); + logger.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + , ip, 5060); + } + + logger.info("[Sip Server] udp://{}:{} 启动成功", monitorIp, port); + } catch (Exception e) { +// } catch (TransportNotSupportedException +// | TooManyListenersException +// | ObjectInUseException +// | InvalidArgumentException e) { + e.printStackTrace(); + logger.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + , monitorIp, port); + } + } + + public SipProviderImpl getUdpSipProvider(String ip) { + if (ObjectUtils.isEmpty(ip)) { + return null; + } + return udpSipProviderMap.get(ip); + } + + public SipProviderImpl getUdpSipProvider() { + if (udpSipProviderMap.size() != 1) { + return null; + } + return udpSipProviderMap.values().stream().findFirst().get(); + } + + public SipProviderImpl getTcpSipProvider() { + if (tcpSipProviderMap.size() != 1) { + return null; + } + return tcpSipProviderMap.values().stream().findFirst().get(); + } + + public SipProviderImpl getTcpSipProvider(String ip) { + if (ObjectUtils.isEmpty(ip)) { + return null; + } + return tcpSipProviderMap.get(ip); + } + + public String getLocalIp(String deviceLocalIp) { + if (!ObjectUtils.isEmpty(deviceLocalIp)) { + return deviceLocalIp; + } + return getUdpSipProvider().getListeningPoint().getIPAddress(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/auth/DigestServerAuthenticationHelper.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/auth/DigestServerAuthenticationHelper.java new file mode 100644 index 0000000..0ecb46b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/auth/DigestServerAuthenticationHelper.java @@ -0,0 +1,241 @@ +/* + * Conditions Of Use + * + * This software was developed by employees of the National Institute of + * Standards and Technology (NIST), an agency of the Federal Government. + * Pursuant to title 15 Untied States Code Section 105, works of NIST + * employees are not subject to copyright protection in the United States + * and are considered to be in the public domain. As a result, a formal + * license is not needed to use the software. + * + * This software is provided by NIST as a service and is expressly + * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED + * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT + * AND DATA ACCURACY. NIST does not warrant or make any representations + * regarding the use of the software or the results thereof, including but + * not limited to the correctness, accuracy, reliability or usefulness of + * the software. + * + * Permission to use this software is contingent upon your acceptance + * of the terms of this agreement + * + * . + * + */ +package com.yfd.monitor.gdw2019.auth; + +import gov.nist.core.InternalErrorHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sip.address.URI; +import javax.sip.header.AuthorizationHeader; +import javax.sip.header.HeaderFactory; +import javax.sip.header.WWWAuthenticateHeader; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Random; + +/** + * Implements the HTTP digest authentication method server side functionality. + * + * + * + */ + +public class DigestServerAuthenticationHelper { + + private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class); + + private MessageDigest messageDigest; + + public static final String DEFAULT_ALGORITHM = "MD5"; + public static final String DEFAULT_SCHEME = "Digest"; + + + + + /** to hex converter */ + private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /** + * Default constructor. + * @throws NoSuchAlgorithmException + */ + public DigestServerAuthenticationHelper() + throws NoSuchAlgorithmException { + messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM); + } + + public static String toHexString(byte[] b) { + int pos = 0; + char[] c = new char[b.length * 2]; + for (int i = 0; i < b.length; i++) { + c[pos++] = toHex[(b[i] >> 4) & 0x0F]; + c[pos++] = toHex[b[i] & 0x0f]; + } + return new String(c); + } + + /** + * Generate the challenge string. + * + * @return a generated nonce. + */ + private String generateNonce() { + long time = Instant.now().toEpochMilli(); + Random rand = new Random(); + long pad = rand.nextLong(); + String nonceString = Long.valueOf(time).toString() + + Long.valueOf(pad).toString(); + byte[] mdbytes = messageDigest.digest(nonceString.getBytes()); + return toHexString(mdbytes); + } + + public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) { + try { + WWWAuthenticateHeader proxyAuthenticate = headerFactory + .createWWWAuthenticateHeader(DEFAULT_SCHEME); + proxyAuthenticate.setParameter("realm", realm); + proxyAuthenticate.setParameter("qop", "auth"); + proxyAuthenticate.setParameter("nonce", generateNonce()); + proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM); + + response.setHeader(proxyAuthenticate); + } catch (Exception ex) { + InternalErrorHandler.handleException(ex); + } + return response; + } + /** + * Authenticate the inbound request. + * + * @param request - the request to authenticate. + * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password. + * + * @return true if authentication succeded and false otherwise. + */ + public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) { + AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); + if ( authHeader == null ) { + return false; + } + String realm = authHeader.getRealm(); + String username = authHeader.getUsername(); + + if ( username == null || realm == null ) { + return false; + } + + String nonce = authHeader.getNonce(); + URI uri = authHeader.getURI(); + if (uri == null) { + return false; + } + + + + String A2 = request.getMethod().toUpperCase() + ":" + uri.toString(); + String HA1 = hashedPassword; + + + byte[] mdbytes = messageDigest.digest(A2.getBytes()); + String HA2 = toHexString(mdbytes); + + String cnonce = authHeader.getCNonce(); + String KD = HA1 + ":" + nonce; + if (cnonce != null) { + KD += ":" + cnonce; + } + KD += ":" + HA2; + mdbytes = messageDigest.digest(KD.getBytes()); + String mdString = toHexString(mdbytes); + String response = authHeader.getResponse(); + + + return mdString.equals(response); + } + + /** + * Authenticate the inbound request given plain text password. + * + * @param request - the request to authenticate. + * @param pass -- the plain text password. + * + * @return true if authentication succeded and false otherwise. + */ + public boolean doAuthenticatePlainTextPassword(Request request, String pass) { + AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); + if ( authHeader == null || authHeader.getRealm() == null) { + return false; + } + String realm = authHeader.getRealm().trim(); + String username = authHeader.getUsername().trim(); + + if ( username == null || realm == null ) { + return false; + } + + String nonce = authHeader.getNonce(); + URI uri = authHeader.getURI(); + if (uri == null) { + return false; + } + // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 + String qop = authHeader.getQop(); + + // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 + // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 + String cnonce = authHeader.getCNonce(); + + // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量 + int nc = authHeader.getNonceCount(); + String ncStr = String.format("%08x", nc).toUpperCase(); + // String ncStr = new DecimalFormat("00000000").format(nc); + // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16)); + + String A1 = username + ":" + realm + ":" + pass; + + String A2 = request.getMethod().toUpperCase() + ":" + uri.toString(); + + byte[] mdbytes = messageDigest.digest(A1.getBytes()); + String HA1 = toHexString(mdbytes); + logger.debug("A1: " + A1); + logger.debug("A2: " + A2); + mdbytes = messageDigest.digest(A2.getBytes()); + String HA2 = toHexString(mdbytes); + logger.debug("HA1: " + HA1); + logger.debug("HA2: " + HA2); + // String cnonce = authHeader.getCNonce(); + logger.debug("nonce: " + nonce); + logger.debug("nc: " + ncStr); + logger.debug("cnonce: " + cnonce); + logger.debug("qop: " + qop); + String KD = HA1 + ":" + nonce; + + if (qop != null && qop.equalsIgnoreCase("auth") ) { + if (nc != -1) { + KD += ":" + ncStr; + } + if (cnonce != null) { + KD += ":" + cnonce; + } + KD += ":" + qop; + } + KD += ":" + HA2; + logger.debug("KD: " + KD); + mdbytes = messageDigest.digest(KD.getBytes()); + String mdString = toHexString(mdbytes); + logger.debug("mdString: " + mdString); + String response = authHeader.getResponse(); + logger.debug("response: " + response); + return mdString.equals(response); + + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AlarmChannelMessage.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AlarmChannelMessage.java new file mode 100644 index 0000000..5bf759f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AlarmChannelMessage.java @@ -0,0 +1,57 @@ +package com.yfd.monitor.gdw2019.bean; + + +/** + * 通过redis分发报警消息 + */ +public class AlarmChannelMessage { + /** + * 国标编号 + */ + private String gbId; + /** + * 报警编号 + */ + private int alarmSn; + /** + * 告警类型 + */ + private int alarmType; + + /** + * 报警描述 + */ + private String alarmDescription; + + public String getGbId() { + return gbId; + } + + public void setGbId(String gbId) { + this.gbId = gbId; + } + + public int getAlarmSn() { + return alarmSn; + } + + public void setAlarmSn(int alarmSn) { + this.alarmSn = alarmSn; + } + + public int getAlarmType() { + return alarmType; + } + + public void setAlarmType(int alarmType) { + this.alarmType = alarmType; + } + + public String getAlarmDescription() { + return alarmDescription; + } + + public void setAlarmDescription(String alarmDescription) { + this.alarmDescription = alarmDescription; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AudioBroadcastCatch.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AudioBroadcastCatch.java new file mode 100644 index 0000000..29db836 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AudioBroadcastCatch.java @@ -0,0 +1,160 @@ +package com.yfd.monitor.gdw2019.bean; + + + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.vmanager.gdw2019.play.bean.AudioBroadcastEvent; +import gov.nist.javax.sip.message.SIPResponse; + +/** + * 缓存语音广播的状态 + * @author lin + */ +public class AudioBroadcastCatch { + + + public AudioBroadcastCatch( + String deviceId, + String channelId, + MediaServerItem mediaServerItem, + String app, + String stream, + AudioBroadcastEvent event, + AudioBroadcastCatchStatus status, + boolean isFromPlatform + ) { + this.deviceId = deviceId; + this.channelId = channelId; + this.status = status; + this.event = event; + this.isFromPlatform = isFromPlatform; + this.app = app; + this.stream = stream; + this.mediaServerItem = mediaServerItem; + } + + public AudioBroadcastCatch() { + } + + /** + * 设备编号 + */ + private String deviceId; + + /** + * 通道编号 + */ + private String channelId; + + /** + * 流媒体信息 + */ + private MediaServerItem mediaServerItem; + + /** + * 关联的流APP + */ + private String app; + + /** + * 关联的流STREAM + */ + private String stream; + + /** + * 是否是级联语音喊话 + */ + private boolean isFromPlatform; + + /** + * 语音广播状态 + */ + private AudioBroadcastCatchStatus status; + + /** + * 请求信息 + */ + private SipTransactionInfo sipTransactionInfo; + + /** + * 请求结果回调 + */ + private AudioBroadcastEvent event; + + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public AudioBroadcastCatchStatus getStatus() { + return status; + } + + public void setStatus(AudioBroadcastCatchStatus status) { + this.status = status; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public boolean isFromPlatform() { + return isFromPlatform; + } + + public void setFromPlatform(boolean fromPlatform) { + isFromPlatform = fromPlatform; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } + + public AudioBroadcastEvent getEvent() { + return event; + } + + public void setEvent(AudioBroadcastEvent event) { + this.event = event; + } + + public void setSipTransactionInfoByRequset(SIPResponse sipResponse) { + this.sipTransactionInfo = new SipTransactionInfo(sipResponse); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AudioBroadcastCatchStatus.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AudioBroadcastCatchStatus.java new file mode 100644 index 0000000..39b28e0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/AudioBroadcastCatchStatus.java @@ -0,0 +1,15 @@ +package com.yfd.monitor.gdw2019.bean; + +/** + * 语音广播状态 + * @author lin + */ +public enum AudioBroadcastCatchStatus { + + // 发送语音广播消息等待对方回复语音广播 + Ready, + // 收到回复等待invite消息 + WaiteInvite, + // 收到invite消息 + Ok, +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/BaiduPoint.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/BaiduPoint.java new file mode 100644 index 0000000..efef69a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/BaiduPoint.java @@ -0,0 +1,24 @@ +package com.yfd.monitor.gdw2019.bean; + +public class BaiduPoint { + + String bdLng; + + String bdLat; + + public String getBdLng() { + return bdLng; + } + + public void setBdLng(String bdLng) { + this.bdLng = bdLng; + } + + public String getBdLat() { + return bdLat; + } + + public void setBdLat(String bdLat) { + this.bdLat = bdLat; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CatalogData.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CatalogData.java new file mode 100644 index 0000000..742c20d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CatalogData.java @@ -0,0 +1,79 @@ +package com.yfd.monitor.gdw2019.bean; + +import java.time.Instant; +import java.util.List; + + +public class CatalogData { + /** + * 命令序列号 + */ + private int sn; + private int total; + private List channelList; + private Instant lastTime; + private Device device; + private String errorMsg; + + public enum CatalogDataStatus{ + ready, runIng, end + } + private CatalogDataStatus status; + + + public int getSn() { + return sn; + } + + public void setSn(int sn) { + this.sn = sn; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public List getChannelList() { + return channelList; + } + + public void setChannelList(List channelList) { + this.channelList = channelList; + } + + public Instant getLastTime() { + return lastTime; + } + + public void setLastTime(Instant lastTime) { + this.lastTime = lastTime; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public CatalogDataStatus getStatus() { + return status; + } + + public void setStatus(CatalogDataStatus status) { + this.status = status; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ChannelIdType.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ChannelIdType.java new file mode 100644 index 0000000..32a7d3c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ChannelIdType.java @@ -0,0 +1,23 @@ +package com.yfd.monitor.gdw2019.bean; + +/** + * 国标类型编码,国标编码中11-13位为类型编码 + * 详见 附 录 D 编码规则 A +* + */ +public class ChannelIdType { + /** + * 中心信令控制服务器编码 + */ + public final static String CENTRAL_SIGNALING_CONTROL_SERVER = "200"; + + /** + * 业务分组编码 + */ + public final static String BUSINESS_GROUP = "215"; + + /** + * 虚拟组织编码 + */ + public final static String VIRTUAL_ORGANIZATION = "216"; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CmdSendFailEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CmdSendFailEvent.java new file mode 100644 index 0000000..bd144b1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CmdSendFailEvent.java @@ -0,0 +1,27 @@ +package com.yfd.monitor.gdw2019.bean; + +import javax.sip.Dialog; +import java.util.EventObject; + +public class CmdSendFailEvent extends EventObject { + + private String callId; + + /** + * Constructs a prototypical Event. + * + * @param dialog + * @throws IllegalArgumentException if source is null. + */ + public CmdSendFailEvent(Dialog dialog) { + super(dialog); + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CmdType.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CmdType.java new file mode 100644 index 0000000..ff2a9d3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/CmdType.java @@ -0,0 +1,8 @@ +package com.yfd.monitor.gdw2019.bean; + +public class CmdType { + + public static final String CATALOG = "Catalog"; + public static final String ALARM = "Alarm"; + public static final String MOBILE_POSITION = "MobilePosition"; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Device.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Device.java new file mode 100644 index 0000000..4093d5c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Device.java @@ -0,0 +1,511 @@ +package com.yfd.monitor.gdw2019.bean; + + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 国标设备/平台 +* + */ +@Schema(description = "国标设备/平台") +public class Device { + /** + * 记录id + */ + @Schema(description = "id") + private String id; + + /** + * 变电站id + */ + @Schema(description = "变电站ID") + private String stationId; + /** + * 设备国标编号 + */ + @Schema(description = "设备国标编号") + private String deviceId; + + /** + * 设备名 + */ + @Schema(description = "名称") + private String name; + + /** + * 生产厂商 + */ + @Schema(description = "生产厂商") + private String manufacturer; + + /** + * 型号 + */ + @Schema(description = "型号") + private String model; + + /** + * 固件版本 + */ + @Schema(description = "固件版本") + private String firmware; + + /** + * 传输协议 + * UDP/TCP + */ + @Schema(description = "传输协议(UDP/TCP)") + private String transport; + + /** + * 数据流传输模式 + * UDP:udp传输 + * TCP-ACTIVE:tcp主动模式 + * TCP-PASSIVE:tcp被动模式 + */ + @Schema(description = "数据流传输模式") + private String streamMode; + + /** + * wan地址_ip + */ + @Schema(description = "IP") + private String ip; + + /** + * wan地址_port + */ + @Schema(description = "端口") + private int port; + + /** + * wan地址 + */ + @Schema(description = "wan地址") + private String hostAddress; + + /** + * 在线 + */ + @Schema(description = "是否在线,1为在线,0为离线") + private int online; + + + /** + * 注册时间 + */ + @Schema(description = "注册时间") + private String registerTime; + + + /** + * 心跳时间 + */ + @Schema(description = "心跳时间") + private String keepaliveTime; + + + /** + * 心跳间隔 + */ + @Schema(description = "心跳间隔") + private int keepaliveIntervalTime; + + /** + * 通道个数 + */ + @Schema(description = "通道个数") + private int channelCount; + + /** + * 注册有效期 + */ + @Schema(description = "注册有效期") + private int expires; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 设备使用的媒体id, 默认为null + */ + @Schema(description = "设备使用的媒体id, 默认为null") + private String mediaServerId; + + /** + * 字符集, 支持 UTF-8 与 GB2312 + */ + @Schema(description = "符集, 支持 UTF-8 与 GB2312") + private String charset ; + + /** + * 目录订阅周期,0为不订阅 + */ + @Schema(description = "目录订阅周期,0为不订阅") + private int subscribeCycleForCatalog; + + /** + * 移动设备位置订阅周期,0为不订阅 + */ + @Schema(description = "移动设备位置订阅周期,0为不订阅") + private int subscribeCycleForMobilePosition; + + /** + * 移动设备位置信息上报时间间隔,单位:秒,默认值5 + */ + @Schema(description = "移动设备位置信息上报时间间隔,单位:秒,默认值5") + private int mobilePositionSubmissionInterval = 5; + + /** + * 报警订阅周期,0为不订阅 + */ + @Schema(description = "报警心跳时间订阅周期,0为不订阅") + private int subscribeCycleForAlarm; + + /** + * 是否开启ssrc校验,默认关闭,开启可以防止串流 + */ + @Schema(description = "是否开启ssrc校验,默认关闭,开启可以防止串流") + private boolean ssrcCheck = true; + + /** + * 地理坐标系, 目前支持 WGS84,GCJ02 + */ + @Schema(description = "地理坐标系, 目前支持 WGS84,GCJ02") + private String geoCoordSys; + + /** + * 树类型 国标规定了两种树的展现方式 行政区划:CivilCode 和业务分组:BusinessGroup + */ + @Schema(description = "树类型 国标规定了两种树的展现方式 行政区划:CivilCode 和业务分组:BusinessGroup") + private String treeType; + + @Schema(description = "密码") + private String password; + + @Schema(description = "收流IP") + private String sdpIp; + + @Schema(description = "SIP交互IP(设备访问平台的IP)") + private String localIp; + + @Schema(description = "是否作为消息通道") + private boolean asMessageChannel; + + @Schema(description = "设备注册的事务信息") + private SipTransactionInfo sipTransactionInfo; + + @Schema(description = "自定义名称") + private String customName; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStationId() { + return stationId; + } + + public void setStationId(String stationId) { + this.stationId = stationId; + } + + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getManufacturer() { + return manufacturer; + } + + public void setManufacturer(String manufacturer) { + this.manufacturer = manufacturer; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getFirmware() { + return firmware; + } + + public void setFirmware(String firmware) { + this.firmware = firmware; + } + + public String getTransport() { + return transport; + } + + public void setTransport(String transport) { + this.transport = transport; + } + + public String getStreamMode() { + return streamMode; + } + + public Integer getStreamModeForParam() { + if (streamMode.equalsIgnoreCase("UDP")) { + return 0; + }else if (streamMode.equalsIgnoreCase("TCP-PASSIVE")) { + return 1; + }else if (streamMode.equalsIgnoreCase("TCP-ACTIVE")) { + return 2; + } + return 0; + } + + public void setStreamMode(String streamMode) { + this.streamMode = streamMode; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getHostAddress() { + return hostAddress; + } + + public void setHostAddress(String hostAddress) { + this.hostAddress = hostAddress; + } + + public int getOnline() { + return online; + } + + public void setOnline(int online) { + this.online = online; + } + + public int getChannelCount() { + return channelCount; + } + + public void setChannelCount(int channelCount) { + this.channelCount = channelCount; + } + + public String getRegisterTime() { + return registerTime; + } + + public void setRegisterTime(String registerTime) { + this.registerTime = registerTime; + } + + public String getKeepaliveTime() { + return keepaliveTime; + } + + public void setKeepaliveTime(String keepaliveTime) { + this.keepaliveTime = keepaliveTime; + } + + public int getExpires() { + return expires; + } + + public void setExpires(int expires) { + this.expires = expires; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public int getSubscribeCycleForCatalog() { + return subscribeCycleForCatalog; + } + + public void setSubscribeCycleForCatalog(int subscribeCycleForCatalog) { + this.subscribeCycleForCatalog = subscribeCycleForCatalog; + } + + public int getSubscribeCycleForMobilePosition() { + return subscribeCycleForMobilePosition; + } + + public void setSubscribeCycleForMobilePosition(int subscribeCycleForMobilePosition) { + this.subscribeCycleForMobilePosition = subscribeCycleForMobilePosition; + } + + public int getMobilePositionSubmissionInterval() { + return mobilePositionSubmissionInterval; + } + + public void setMobilePositionSubmissionInterval(int mobilePositionSubmissionInterval) { + this.mobilePositionSubmissionInterval = mobilePositionSubmissionInterval; + } + + public int getSubscribeCycleForAlarm() { + return subscribeCycleForAlarm; + } + + public void setSubscribeCycleForAlarm(int subscribeCycleForAlarm) { + this.subscribeCycleForAlarm = subscribeCycleForAlarm; + } + + public boolean isSsrcCheck() { + return ssrcCheck; + } + + public void setSsrcCheck(boolean ssrcCheck) { + this.ssrcCheck = ssrcCheck; + } + + public String getGeoCoordSys() { + return geoCoordSys; + } + + public void setGeoCoordSys(String geoCoordSys) { + this.geoCoordSys = geoCoordSys; + } + + public String getTreeType() { + return treeType; + } + + public void setTreeType(String treeType) { + this.treeType = treeType; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getLocalIp() { + return localIp; + } + + public void setLocalIp(String localIp) { + this.localIp = localIp; + } + + public int getKeepaliveIntervalTime() { + return keepaliveIntervalTime; + } + + public void setKeepaliveIntervalTime(int keepaliveIntervalTime) { + this.keepaliveIntervalTime = keepaliveIntervalTime; + } + + public boolean isAsMessageChannel() { + return asMessageChannel; + } + + public void setAsMessageChannel(boolean asMessageChannel) { + this.asMessageChannel = asMessageChannel; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } + + public String getCustomName() { + return customName; + } + public void setCustomName(String customName) { + this.customName = customName; + } + + @Schema(description = "开启主子码流切换的开关(false-不开启,true-开启)") + private boolean switchPrimarySubStream; + + public boolean isSwitchPrimarySubStream() { + return switchPrimarySubStream; + } + + public void setSwitchPrimarySubStream(boolean switchPrimarySubStream) { + this.switchPrimarySubStream = switchPrimarySubStream; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceAlarm.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceAlarm.java new file mode 100644 index 0000000..aec8129 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceAlarm.java @@ -0,0 +1,185 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + + +@Schema(description = "报警信息") +public class DeviceAlarm { + + /** + * 数据库id + */ + @Schema(description = "数据库id") + private String id; + + /** + * 设备Id + */ + @Schema(description = "设备的国标编号") + private String deviceId; + + /** + * 通道Id + */ + @Schema(description = "通道的国标编号") + private String channelId; + + /** + * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情 + */ + @Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情") + private String alarmPriority; + + /** + * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, + * 7其他报警;可以为直接组合如12为电话报警或 设备报警- + */ + @Schema(description = "报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,\n" + + "\t * 7其他报警;可以为直接组合如12为电话报警或设备报警") + private String alarmMethod; + + /** + * 报警时间 + */ + @Schema(description = "报警时间") + private String alarmTime; + + /** + * 报警内容描述 + */ + @Schema(description = "报警内容描述") + private String alarmDescription; + + /** + * 经度 + */ + @Schema(description = "经度") + private double longitude; + + /** + * 纬度 + */ + @Schema(description = "纬度") + private double latitude; + + /** + * 报警类型, + * 报警方式为2时,不携带 AlarmType为默认的报警设备报警, + * 携带 AlarmType取值及对应报警类型如下: + * 1-视频丢失报警; + * 2-设备防拆报警; + * 3-存储设备磁盘满报警; + * 4-设备高温报警; + * 5-设备低温报警。 + * 报警方式为5时,取值如下: + * 1-人工视频报警; + * 2-运动目标检测报警; + * 3-遗留物检测报警; + * 4-物体移除检测报警; + * 5-绊线检测报警; + * 6-入侵检测报警; + * 7-逆行检测报警; + * 8-徘徊检测报警; + * 9-流量统计报警; + * 10-密度检测报警; + * 11-视频异常检测报警; + * 12-快速移动报警。 + * 报警方式为6时,取值下: + * 1-存储设备磁盘故障报警; + * 2-存储设备风扇故障报警。 + */ + @Schema(description = "报警类型") + private String alarmType; + + @Schema(description = "创建时间") + private String createTime; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getAlarmPriority() { + return alarmPriority; + } + + public void setAlarmPriority(String alarmPriority) { + this.alarmPriority = alarmPriority; + } + + public String getAlarmMethod() { + return alarmMethod; + } + + public void setAlarmMethod(String alarmMethod) { + this.alarmMethod = alarmMethod; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public String getAlarmDescription() { + return alarmDescription; + } + + public void setAlarmDescription(String alarmDescription) { + this.alarmDescription = alarmDescription; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public String getAlarmType() { + return alarmType; + } + + public void setAlarmType(String alarmType) { + this.alarmType = alarmType; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceAlarmMethod.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceAlarmMethod.java new file mode 100644 index 0000000..07d14ab --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceAlarmMethod.java @@ -0,0 +1,54 @@ +package com.yfd.monitor.gdw2019.bean; + +/** + * 报警方式 +* + * 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, + * 7其他报警;可以为直接组合如12为电话报警或 设备报警- + */ +public enum DeviceAlarmMethod { + // 1为电话报警 + Telephone(1), + + // 2为设备报警 + Device(2), + + // 3为短信报警 + SMS(3), + + // 4为 GPS报警 + GPS(4), + + // 5为视频报警 + Video(5), + + // 6为设备故障报警 + DeviceFailure(6), + + // 7其他报警 + Other(7); + + private final int val; + + DeviceAlarmMethod(int val) { + this.val=val; + } + + public int getVal() { + return val; + } + + /** + * 查询是否匹配类型 + * @param code + * @return + */ + public static DeviceAlarmMethod typeOf(int code) { + for (DeviceAlarmMethod item : DeviceAlarmMethod.values()) { + if (code==item.getVal()) { + return item; + } + } + return null; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceChannel.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceChannel.java new file mode 100644 index 0000000..c6e324a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceChannel.java @@ -0,0 +1,577 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "通道信息") +public class DeviceChannel { + + + /** + * 数据库自增ID + */ + @Schema(description = "数据库自增ID") + private int id; + + /** + * 通道国标编号 + */ + @Schema(description = "通道国标编号") + private String channelId; + + /** + * 设备国标编号 + */ + @Schema(description = "设备国标编号") + private String deviceId; + + /** + * 通道名 + */ + @Schema(description = "名称") + private String name; + + /** + * 生产厂商 + */ + @Schema(description = "生产厂商") + private String manufacture; + + /** + * 型号 + */ + @Schema(description = "型号") + private String model; + + /** + * 设备归属 + */ + @Schema(description = "设备归属") + private String owner; + + /** + * 行政区域 + */ + @Schema(description = "行政区域") + private String civilCode; + + /** + * 警区 + */ + @Schema(description = "警区") + private String block; + + /** + * 安装地址 + */ + @Schema(description = "安装地址") + private String address; + + /** + * 是否有子设备 1有, 0没有 + */ + @Schema(description = "是否有子设备 1有, 0没有") + private int parental; + + /** + * 父级id + */ + @Schema(description = "父级id") + private String parentId; + + /** + * 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式 + */ + @Schema(description = "信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式") + private int safetyWay; + + /** + * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式 + */ + @Schema(description = "注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式") + private int registerWay; + + /** + * 证书序列号 + */ + @Schema(description = "证书序列号") + private String certNum; + + /** + * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 + */ + @Schema(description = "证书有效标识 缺省为0;证书有效标识:0:无效1: 有效") + private int certifiable; + + /** + * 证书无效原因码 + */ + @Schema(description = "证书无效原因码") + private int errCode; + + /** + * 证书终止有效期 + */ + @Schema(description = "证书终止有效期") + private String endTime; + + /** + * 保密属性 缺省为0; 0:不涉密, 1:涉密 + */ + @Schema(description = "保密属性 缺省为0; 0:不涉密, 1:涉密") + private String secrecy; + + /** + * IP地址 + */ + @Schema(description = "IP地址") + private String ipAddress; + + /** + * 端口号 + */ + @Schema(description = "端口号") + private int port; + + /** + * 密码 + */ + @Schema(description = "密码") + private String password; + + /** + * 云台类型 + */ + @Schema(description = "云台类型") + private int PTZType; + + /** + * 云台类型描述字符串 + */ + @Schema(description = "云台类型描述字符串") + private String PTZTypeText; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 在线/离线 + * 1在线,0离线 + * 默认在线 + * 信令: + * ON + * OFF + * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF + */ + @Schema(description = "在线/离线, 1在线,0离线") + private int status; + + /** + * 经度 + */ + @Schema(description = "经度") + private double longitude; + + /** + * 纬度 + */ + @Schema(description = "纬度") + private double latitude; + + /** + * 经度 GCJ02 + */ + @Schema(description = "GCJ02坐标系经度") + private double longitudeGcj02; + + /** + * 纬度 GCJ02 + */ + @Schema(description = "GCJ02坐标系纬度") + private double latitudeGcj02; + + /** + * 经度 WGS84 + */ + @Schema(description = "WGS84坐标系经度") + private double longitudeWgs84; + + /** + * 纬度 WGS84 + */ + @Schema(description = "WGS84坐标系纬度") + private double latitudeWgs84; + + /** + * 子设备数 + */ + @Schema(description = "子设备数") + private int subCount; + + /** + * 流唯一编号,存在表示正在直播 + */ + @Schema(description = "流唯一编号,存在表示正在直播") + private String streamId; + + /** + * 是否含有音频 + */ + @Schema(description = "是否含有音频") + private boolean hasAudio; + + /** + * 标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 + */ + @Schema(description = "标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划") + private int channelType; + + /** + * 业务分组 + */ + @Schema(description = "业务分组") + private String businessGroupId; + + /** + * GPS的更新时间 + */ + @Schema(description = "GPS的更新时间") + private String gpsTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public void setPTZType(int PTZType) { + this.PTZType = PTZType; + switch (PTZType) { + case 0: + this.PTZTypeText = "未知"; + break; + case 1: + this.PTZTypeText = "球机"; + break; + case 2: + this.PTZTypeText = "半球"; + break; + case 3: + this.PTZTypeText = "固定枪机"; + break; + case 4: + this.PTZTypeText = "遥控枪机"; + break; + } + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getManufacture() { + return manufacture; + } + + public void setManufacture(String manufacture) { + this.manufacture = manufacture; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getCivilCode() { + return civilCode; + } + + public void setCivilCode(String civilCode) { + this.civilCode = civilCode; + } + + public String getBlock() { + return block; + } + + public void setBlock(String block) { + this.block = block; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getParental() { + return parental; + } + + public void setParental(int parental) { + this.parental = parental; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public int getSafetyWay() { + return safetyWay; + } + + public void setSafetyWay(int safetyWay) { + this.safetyWay = safetyWay; + } + + public int getRegisterWay() { + return registerWay; + } + + public void setRegisterWay(int registerWay) { + this.registerWay = registerWay; + } + + public String getCertNum() { + return certNum; + } + + public void setCertNum(String certNum) { + this.certNum = certNum; + } + + public int getCertifiable() { + return certifiable; + } + + public void setCertifiable(int certifiable) { + this.certifiable = certifiable; + } + + public int getErrCode() { + return errCode; + } + + public void setErrCode(int errCode) { + this.errCode = errCode; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getSecrecy() { + return secrecy; + } + + public void setSecrecy(String secrecy) { + this.secrecy = secrecy; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getPTZType() { + return PTZType; + } + + public String getPTZTypeText() { + return PTZTypeText; + } + + public void setPTZTypeText(String PTZTypeText) { + this.PTZTypeText = PTZTypeText; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getLongitudeGcj02() { + return longitudeGcj02; + } + + public void setLongitudeGcj02(double longitudeGcj02) { + this.longitudeGcj02 = longitudeGcj02; + } + + public double getLatitudeGcj02() { + return latitudeGcj02; + } + + public void setLatitudeGcj02(double latitudeGcj02) { + this.latitudeGcj02 = latitudeGcj02; + } + + public double getLongitudeWgs84() { + return longitudeWgs84; + } + + public void setLongitudeWgs84(double longitudeWgs84) { + this.longitudeWgs84 = longitudeWgs84; + } + + public double getLatitudeWgs84() { + return latitudeWgs84; + } + + public void setLatitudeWgs84(double latitudeWgs84) { + this.latitudeWgs84 = latitudeWgs84; + } + + public int getSubCount() { + return subCount; + } + + public void setSubCount(int subCount) { + this.subCount = subCount; + } + + public boolean isHasAudio() { + return hasAudio; + } + + public void setHasAudio(boolean hasAudio) { + this.hasAudio = hasAudio; + } + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getChannelType() { + return channelType; + } + + public void setChannelType(int channelType) { + this.channelType = channelType; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } + + public String getGpsTime() { + return gpsTime; + } + + public void setGpsTime(String gpsTime) { + this.gpsTime = gpsTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceChannelInPlatform.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceChannelInPlatform.java new file mode 100644 index 0000000..e54cf5f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceChannelInPlatform.java @@ -0,0 +1,23 @@ +package com.yfd.monitor.gdw2019.bean; + +public class DeviceChannelInPlatform extends DeviceChannel{ + + private String platFormId; + private String catalogId; + + public String getPlatFormId() { + return platFormId; + } + + public void setPlatFormId(String platFormId) { + this.platFormId = platFormId; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceNotFoundEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceNotFoundEvent.java new file mode 100644 index 0000000..5fbae57 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DeviceNotFoundEvent.java @@ -0,0 +1,27 @@ +package com.yfd.monitor.gdw2019.bean; + +import javax.sip.Dialog; +import java.util.EventObject; + +public class DeviceNotFoundEvent extends EventObject { + + private String callId; + + /** + * Constructs a prototypical Event. + * + * @param dialog + * @throws IllegalArgumentException if source is null. + */ + public DeviceNotFoundEvent(Dialog dialog) { + super(dialog); + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DragZoomRequest.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DragZoomRequest.java new file mode 100644 index 0000000..813ff7e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/DragZoomRequest.java @@ -0,0 +1,142 @@ +package com.yfd.monitor.gdw2019.bean; + +import com.yfd.monitor.gdw2019.utils.MessageElement; + +/** + * 设备信息查询响应 + * + * @version 1.0 + * @date 2022/6/28 14:55 + */ +public class DragZoomRequest { + /** + * 序列号 + */ + @MessageElement("SN") + private String sn; + + @MessageElement("DeviceID") + private String deviceId; + + @MessageElement(value = "DragZoomIn") + private DragZoom dragZoomIn; + + @MessageElement(value = "DragZoomOut") + private DragZoom dragZoomOut; + + /** + * 基本参数 + */ + public static class DragZoom { + /** + * 播放窗口长度像素值 + */ + @MessageElement("Length") + protected Integer length; + /** + * 播放窗口宽度像素值 + */ + @MessageElement("Width") + protected Integer width; + /** + * 拉框中心的横轴坐标像素值 + */ + @MessageElement("MidPointX") + protected Integer midPointX; + /** + * 拉框中心的纵轴坐标像素值 + */ + @MessageElement("MidPointY") + protected Integer midPointY; + /** + * 拉框长度像素值 + */ + @MessageElement("LengthX") + protected Integer lengthX; + /** + * 拉框宽度像素值 + */ + @MessageElement("LengthY") + protected Integer lengthY; + + public Integer getLength() { + return length; + } + + public void setLength(Integer length) { + this.length = length; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getMidPointX() { + return midPointX; + } + + public void setMidPointX(Integer midPointX) { + this.midPointX = midPointX; + } + + public Integer getMidPointY() { + return midPointY; + } + + public void setMidPointY(Integer midPointY) { + this.midPointY = midPointY; + } + + public Integer getLengthX() { + return lengthX; + } + + public void setLengthX(Integer lengthX) { + this.lengthX = lengthX; + } + + public Integer getLengthY() { + return lengthY; + } + + public void setLengthY(Integer lengthY) { + this.lengthY = lengthY; + } + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public DragZoom getDragZoomIn() { + return dragZoomIn; + } + + public void setDragZoomIn(DragZoom dragZoomIn) { + this.dragZoomIn = dragZoomIn; + } + + public DragZoom getDragZoomOut() { + return dragZoomOut; + } + + public void setDragZoomOut(DragZoom dragZoomOut) { + this.dragZoomOut = dragZoomOut; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Gb28181Sdp.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Gb28181Sdp.java new file mode 100644 index 0000000..2c09417 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Gb28181Sdp.java @@ -0,0 +1,46 @@ +package com.yfd.monitor.gdw2019.bean; + +import javax.sdp.SessionDescription; + +/** + * 28181 的SDP解析器 + */ +public class Gb28181Sdp { + private SessionDescription baseSdp; + private String ssrc; + + private String mediaDescription; + + public static Gb28181Sdp getInstance(SessionDescription baseSdp, String ssrc, String mediaDescription) { + Gb28181Sdp gb28181Sdp = new Gb28181Sdp(); + gb28181Sdp.setBaseSdp(baseSdp); + gb28181Sdp.setSsrc(ssrc); + gb28181Sdp.setMediaDescription(mediaDescription); + return gb28181Sdp; + } + + + public SessionDescription getBaseSdp() { + return baseSdp; + } + + public void setBaseSdp(SessionDescription baseSdp) { + this.baseSdp = baseSdp; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getMediaDescription() { + return mediaDescription; + } + + public void setMediaDescription(String mediaDescription) { + this.mediaDescription = mediaDescription; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/GbStream.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/GbStream.java new file mode 100644 index 0000000..00c9a3c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/GbStream.java @@ -0,0 +1,125 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 直播流关联国标上级平台 +* + */ +@Schema(description = "直播流关联国标上级平台") +public class GbStream extends PlatformGbStream{ + + @Schema(description = "ID") + private Integer gbStreamId; + @Schema(description = "应用名") + private String app; + @Schema(description = "流ID") + private String stream; + @Schema(description = "国标ID") + private String gbId; + @Schema(description = "名称") + private String name; + @Schema(description = "流媒体ID") + private String mediaServerId; + @Schema(description = "经度") + private double longitude; + @Schema(description = "纬度") + private double latitude; + @Schema(description = "流类型(拉流/推流)") + private String streamType; + @Schema(description = "状态") + private boolean status; + + @Schema(description = "创建时间") + public String createTime; + + @Override + public Integer getGbStreamId() { + return gbStreamId; + } + + @Override + public void setGbStreamId(Integer gbStreamId) { + this.gbStreamId = gbStreamId; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getGbId() { + return gbId; + } + + public void setGbId(String gbId) { + this.gbId = gbId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public String getStreamType() { + return streamType; + } + + public void setStreamType(String streamType) { + this.streamType = streamType; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/HandlerCatchData.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/HandlerCatchData.java new file mode 100644 index 0000000..2410b68 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/HandlerCatchData.java @@ -0,0 +1,42 @@ +package com.yfd.monitor.gdw2019.bean; + +import org.dom4j.Element; + +import javax.sip.RequestEvent; + + +public class HandlerCatchData { + private RequestEvent evt; + private Device device; + private Element rootElement; + + public HandlerCatchData(RequestEvent evt, Device device, Element rootElement) { + this.evt = evt; + this.device = device; + this.rootElement = rootElement; + } + + public RequestEvent getEvt() { + return evt; + } + + public void setEvt(RequestEvent evt) { + this.evt = evt; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + public Element getRootElement() { + return rootElement; + } + + public void setRootElement(Element rootElement) { + this.rootElement = rootElement; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/HomePositionRequest.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/HomePositionRequest.java new file mode 100644 index 0000000..9807534 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/HomePositionRequest.java @@ -0,0 +1,93 @@ +package com.yfd.monitor.gdw2019.bean; + +import com.yfd.monitor.gdw2019.utils.MessageElement; + +/** + * 设备信息查询响应 + * + * @version 1.0 + * @date 2022/6/28 14:55 + */ +public class HomePositionRequest { + /** + * 序列号 + */ + @MessageElement("SN") + private String sn; + + @MessageElement("DeviceID") + private String deviceId; + + @MessageElement(value = "HomePosition") + private HomePosition homePosition; + + + /** + * 基本参数 + */ + public static class HomePosition { + /** + * 播放窗口长度像素值 + */ + @MessageElement("Enabled") + protected String enabled; + /** + * 播放窗口宽度像素值 + */ + @MessageElement("ResetTime") + protected String resetTime; + /** + * 拉框中心的横轴坐标像素值 + */ + @MessageElement("PresetIndex") + protected String presetIndex; + + public String getEnabled() { + return enabled; + } + + public void setEnabled(String enabled) { + this.enabled = enabled; + } + + public String getResetTime() { + return resetTime; + } + + public void setResetTime(String resetTime) { + this.resetTime = resetTime; + } + + public String getPresetIndex() { + return presetIndex; + } + + public void setPresetIndex(String presetIndex) { + this.presetIndex = presetIndex; + } + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public HomePosition getHomePosition() { + return homePosition; + } + + public void setHomePosition(HomePosition homePosition) { + this.homePosition = homePosition; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Host.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Host.java new file mode 100644 index 0000000..7242b3f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/Host.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.gdw2019.bean; + + + +public class Host { + + private String ip; + private int port; + private String address; + + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamCallback.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamCallback.java new file mode 100644 index 0000000..967f790 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamCallback.java @@ -0,0 +1,5 @@ +package com.yfd.monitor.gdw2019.bean; + +public interface InviteStreamCallback { + void call(InviteStreamInfo inviteStreamInfo); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamInfo.java new file mode 100644 index 0000000..ba91a71 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamInfo.java @@ -0,0 +1,61 @@ +package com.yfd.monitor.gdw2019.bean; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; + +public class InviteStreamInfo { + + public InviteStreamInfo(MediaServerItem mediaServerItem, JSONObject response, String callId, String app, String stream) { + this.mediaServerItem = mediaServerItem; + this.response = response; + this.callId = callId; + this.app = app; + this.stream = stream; + } + + private MediaServerItem mediaServerItem; + private JSONObject response; + private String callId; + private String app; + private String stream; + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } + + public JSONObject getResponse() { + return response; + } + + public void setResponse(JSONObject response) { + this.response = response; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamType.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamType.java new file mode 100644 index 0000000..3008170 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/InviteStreamType.java @@ -0,0 +1,8 @@ +package com.yfd.monitor.gdw2019.bean; + +public enum InviteStreamType { + + PLAY,TALK,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/MobilePosition.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/MobilePosition.java new file mode 100644 index 0000000..6318a40 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/MobilePosition.java @@ -0,0 +1,205 @@ +package com.yfd.monitor.gdw2019.bean; + +/** + * @description: 移动位置bean + * + * @date: 2021年1月23日 + */ + +public class MobilePosition { + /** + * 设备Id + */ + private String deviceId; + + /** + * 通道Id + */ + private String channelId; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 通知时间 + */ + private String time; + + /** + * 经度 + */ + private double longitude; + + /** + * 纬度 + */ + private double latitude; + + /** + * 海拔高度 + */ + private double altitude; + + /** + * 速度 + */ + private double speed; + + /** + * 方向 + */ + private double direction; + + /** + * 位置信息上报来源(Mobile Position、GPS Alarm) + */ + private String reportSource; + + /** + * 国内坐标系:经度坐标 + */ + private double longitudeGcj02; + + /** + * 国内坐标系:纬度坐标 + */ + private double latitudeGcj02; + + /** + * 国内坐标系:经度坐标 + */ + private double longitudeWgs84; + + /** + * 国内坐标系:纬度坐标 + */ + private double latitudeWgs84; + + /** + * 创建时间 + */ + private String createTime; + + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getAltitude() { + return altitude; + } + + public void setAltitude(double altitude) { + this.altitude = altitude; + } + + public double getSpeed() { + return speed; + } + + public void setSpeed(double speed) { + this.speed = speed; + } + + public double getDirection() { + return direction; + } + + public void setDirection(double direction) { + this.direction = direction; + } + + public String getReportSource() { + return reportSource; + } + + public void setReportSource(String reportSource) { + this.reportSource = reportSource; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public double getLongitudeGcj02() { + return longitudeGcj02; + } + + public void setLongitudeGcj02(double longitudeGcj02) { + this.longitudeGcj02 = longitudeGcj02; + } + + public double getLatitudeGcj02() { + return latitudeGcj02; + } + + public void setLatitudeGcj02(double latitudeGcj02) { + this.latitudeGcj02 = latitudeGcj02; + } + + public double getLongitudeWgs84() { + return longitudeWgs84; + } + + public void setLongitudeWgs84(double longitudeWgs84) { + this.longitudeWgs84 = longitudeWgs84; + } + + public double getLatitudeWgs84() { + return latitudeWgs84; + } + + public void setLatitudeWgs84(double latitudeWgs84) { + this.latitudeWgs84 = latitudeWgs84; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ParentPlatform.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ParentPlatform.java new file mode 100644 index 0000000..cef8443 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ParentPlatform.java @@ -0,0 +1,437 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + + +@Schema(description = "平台信息") +public class ParentPlatform { + + /** + * id + */ + @Schema(description = "ID(数据库中)") + private Integer id; + + /** + * 是否启用 + */ + @Schema(description = "是否启用") + private boolean enable; + + /** + * 名称 + */ + @Schema(description = "名称") + private String name; + + /** + * SIP服务国标编码 + */ + @Schema(description = "SIP服务国标编码") + private String serverGBId; + + /** + * SIP服务国标域 + */ + @Schema(description = "SIP服务国标域") + private String serverGBDomain; + + /** + * SIP服务IP + */ + @Schema(description = "SIP服务IP") + private String serverIP; + + /** + * SIP服务端口 + */ + @Schema(description = "SIP服务端口") + private int serverPort; + + /** + * 设备国标编号 + */ + @Schema(description = "设备国标编号") + private String deviceGBId; + + /** + * 设备ip + */ + @Schema(description = "设备ip") + private String deviceIp; + + /** + * 设备端口 + */ + @Schema(description = "设备端口") + private String devicePort; + + /** + * SIP认证用户名(默认使用设备国标编号) + */ + @Schema(description = "SIP认证用户名(默认使用设备国标编号)") + private String username; + + /** + * SIP认证密码 + */ + @Schema(description = "SIP认证密码") + private String password; + + /** + * 注册周期 (秒) + */ + @Schema(description = "注册周期 (秒)") + private int expires; + + /** + * 心跳周期(秒) + */ + @Schema(description = "心跳周期(秒)") + private int keepTimeout; + + /** + * 传输协议 + * UDP/TCP + */ + @Schema(description = "传输协议") + private String transport; + + /** + * 字符集 + */ + @Schema(description = "字符集") + private String characterSet; + + /** + * 允许云台控制 + */ + @Schema(description = "允许云台控制") + private boolean ptz; + + /** + * RTCP流保活 + */ + @Schema(description = "RTCP流保活") + private boolean rtcp; + + /** + * 在线状态 + */ + @Schema(description = "在线状态") + private boolean status; + + /** + * 在线状态 + */ + @Schema(description = "在线状态") + private int channelCount; + + /** + * 默认目录Id,自动添加的通道多放在这个目录下 + */ + @Schema(description = "默认目录Id,自动添加的通道多放在这个目录下") + private String catalogId; + + /** + * 已被订阅目录信息 + */ + @Schema(description = "已被订阅目录信息") + private boolean catalogSubscribe; + + /** + * 已被订阅报警信息 + */ + @Schema(description = "已被订阅报警信息") + private boolean alarmSubscribe; + + /** + * 已被订阅移动位置信息 + */ + @Schema(description = "已被订阅移动位置信息") + private boolean mobilePositionSubscribe; + + /** + * 点播未推流的设备时是否使用redis通知拉起 + */ + @Schema(description = "点播未推流的设备时是否使用redis通知拉起") + private boolean startOfflinePush; + + /** + * 目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8 + */ + @Schema(description = "目录分组-每次向上级发送通道信息时单个包携带的通道数量,取值1,2,4,8") + private int catalogGroup; + + /** + * 行政区划 + */ + @Schema(description = "行政区划") + private String administrativeDivision; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + @Schema(description = "是否作为消息通道") + private boolean asMessageChannel; + + @Schema(description = "是否作为消息通道") + private boolean autoPushChannel; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getServerGBId() { + return serverGBId; + } + + public void setServerGBId(String serverGBId) { + this.serverGBId = serverGBId; + } + + public String getServerGBDomain() { + return serverGBDomain; + } + + public void setServerGBDomain(String serverGBDomain) { + this.serverGBDomain = serverGBDomain; + } + + public String getServerIP() { + return serverIP; + } + + public void setServerIP(String serverIP) { + this.serverIP = serverIP; + } + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public String getDeviceGBId() { + return deviceGBId; + } + + public void setDeviceGBId(String deviceGBId) { + this.deviceGBId = deviceGBId; + } + + public String getDeviceIp() { + return deviceIp; + } + + public void setDeviceIp(String deviceIp) { + this.deviceIp = deviceIp; + } + + public String getDevicePort() { + return devicePort; + } + + public void setDevicePort(String devicePort) { + this.devicePort = devicePort; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getExpires() { + return expires; + } + + public void setExpires(int expires) { + this.expires = expires; + } + + public int getKeepTimeout() { + return keepTimeout; + } + + public void setKeepTimeout(int keepTimeout) { + this.keepTimeout = keepTimeout; + } + + public String getTransport() { + return transport; + } + + public void setTransport(String transport) { + this.transport = transport; + } + + public String getCharacterSet() { + return characterSet; + } + + public void setCharacterSet(String characterSet) { + this.characterSet = characterSet; + } + + public boolean isPtz() { + return ptz; + } + + public void setPtz(boolean ptz) { + this.ptz = ptz; + } + + public boolean isRtcp() { + return rtcp; + } + + public void setRtcp(boolean rtcp) { + this.rtcp = rtcp; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public int getChannelCount() { + return channelCount; + } + + public void setChannelCount(int channelCount) { + this.channelCount = channelCount; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public boolean isCatalogSubscribe() { + return catalogSubscribe; + } + + public void setCatalogSubscribe(boolean catalogSubscribe) { + this.catalogSubscribe = catalogSubscribe; + } + + public boolean isAlarmSubscribe() { + return alarmSubscribe; + } + + public void setAlarmSubscribe(boolean alarmSubscribe) { + this.alarmSubscribe = alarmSubscribe; + } + + public boolean isMobilePositionSubscribe() { + return mobilePositionSubscribe; + } + + public void setMobilePositionSubscribe(boolean mobilePositionSubscribe) { + this.mobilePositionSubscribe = mobilePositionSubscribe; + } + + public boolean isStartOfflinePush() { + return startOfflinePush; + } + + public void setStartOfflinePush(boolean startOfflinePush) { + this.startOfflinePush = startOfflinePush; + } + + public int getCatalogGroup() { + return catalogGroup; + } + + public void setCatalogGroup(int catalogGroup) { + this.catalogGroup = catalogGroup; + } + + public String getAdministrativeDivision() { + return administrativeDivision; + } + + public void setAdministrativeDivision(String administrativeDivision) { + this.administrativeDivision = administrativeDivision; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public boolean isAsMessageChannel() { + return asMessageChannel; + } + + public void setAsMessageChannel(boolean asMessageChannel) { + this.asMessageChannel = asMessageChannel; + } + + public boolean isAutoPushChannel() { + return autoPushChannel; + } + + public void setAutoPushChannel(boolean autoPushChannel) { + this.autoPushChannel = autoPushChannel; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ParentPlatformCatch.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ParentPlatformCatch.java new file mode 100644 index 0000000..af5845d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/ParentPlatformCatch.java @@ -0,0 +1,73 @@ +package com.yfd.monitor.gdw2019.bean; + +import javax.sip.header.WWWAuthenticateHeader; + +public class ParentPlatformCatch { + + private String id; + + /** + * 心跳未回复次数 + */ + private int keepAliveReply; + + // 注册未回复次数 + private int registerAliveReply; + + private String callId; + + private ParentPlatform parentPlatform; + + private SipTransactionInfo sipTransactionInfo; + + private WWWAuthenticateHeader www; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getKeepAliveReply() { + return keepAliveReply; + } + + public void setKeepAliveReply(int keepAliveReply) { + this.keepAliveReply = keepAliveReply; + } + + public int getRegisterAliveReply() { + return registerAliveReply; + } + + public void setRegisterAliveReply(int registerAliveReply) { + this.registerAliveReply = registerAliveReply; + } + + public ParentPlatform getParentPlatform() { + return parentPlatform; + } + + public void setParentPlatform(ParentPlatform parentPlatform) { + this.parentPlatform = parentPlatform; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformCatalog.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformCatalog.java new file mode 100644 index 0000000..9cb52de --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformCatalog.java @@ -0,0 +1,115 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 国标级联-目录 + */ +@Schema(description = "目录信息") +public class PlatformCatalog { + @Schema(description = "ID") + private String id; + + @Schema(description = "名称") + private String name; + + @Schema(description = "平台ID") + private String platformId; + + @Schema(description = "父级目录ID") + private String parentId; + + @Schema(description = "行政区划") + private String civilCode; + + @Schema(description = "目录分组") + private String businessGroupId; + + /** + * 子节点数 + */ + @Schema(description = "子节点数") + private int childrenCount; + + /** + * 0 目录, 1 国标通道, 2 直播流 + */ + @Schema(description = "类型:0 目录, 1 国标通道, 2 直播流") + private int type; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public int getChildrenCount() { + return childrenCount; + } + + public void setChildrenCount(int childrenCount) { + this.childrenCount = childrenCount; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public void setTypeForCatalog() { + this.type = 0; + } + + public void setTypeForGb() { + this.type = 1; + } + + public void setTypeForStream() { + this.type = 2; + } + + public String getCivilCode() { + return civilCode; + } + + public void setCivilCode(String civilCode) { + this.civilCode = civilCode; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformGbStream.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformGbStream.java new file mode 100644 index 0000000..eb31894 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformGbStream.java @@ -0,0 +1,39 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +public class PlatformGbStream { + + @Schema(description = "ID") + private Integer gbStreamId; + + @Schema(description = "平台ID") + private String platformId; + + @Schema(description = "目录ID") + private String catalogId; + + public Integer getGbStreamId() { + return gbStreamId; + } + + public void setGbStreamId(Integer gbStreamId) { + this.gbStreamId = gbStreamId; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformRegister.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformRegister.java new file mode 100644 index 0000000..786ba4d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PlatformRegister.java @@ -0,0 +1,15 @@ +package com.yfd.monitor.gdw2019.bean; + +public class PlatformRegister { + + // 未回复次数 + private int reply; + + public int getReply() { + return reply; + } + + public void setReply(int reply) { + this.reply = reply; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PresetQuerySipReq.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PresetQuerySipReq.java new file mode 100644 index 0000000..5f1dea4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/PresetQuerySipReq.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.gdw2019.bean; + + +public class PresetQuerySipReq { + + private String presetId; + + private String presetName; + + public String getPresetId() { + return presetId; + } + + public void setPresetId(String presetId) { + this.presetId = presetId; + } + + public String getPresetName() { + return presetName; + } + + public void setPresetName(String presetName) { + this.presetName = presetName; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RecordInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RecordInfo.java new file mode 100644 index 0000000..0f4dcf4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RecordInfo.java @@ -0,0 +1,113 @@ +package com.yfd.monitor.gdw2019.bean; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.time.Instant; +import java.util.List; + +/** + * @description:设备录像信息bean +* + * @date: 2020年5月8日 下午2:05:56 + */ +@Schema(description = "设备录像查询结果信息") +public class RecordInfo { + + @Schema(description = "设备编号") + private String deviceId; + + @Schema(description = "通道编号") + private String channelId; + + @Schema(description = "命令序列号") + private String sn; + + @Schema(description = "设备名称") + private String name; + + @Schema(description = "列表总数") + private int sumNum; + + @Schema(description = "总时长") + private long recordTimes; + + private int count; + + private Instant lastTime; + + @Schema(description = "") + private List recordList; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSumNum() { + return sumNum; + } + + public void setSumNum(int sumNum) { + this.sumNum = sumNum; + } + + public List getRecordList() { + return recordList; + } + + public void setRecordList(List recordList) { + this.recordList = recordList; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public Instant getLastTime() { + return lastTime; + } + + public void setLastTime(Instant lastTime) { + this.lastTime = lastTime; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public long getRecordTimes() { + return recordTimes; + } + + public void setRecordTimes(long recordTimes) { + this.recordTimes = recordTimes; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RecordItem.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RecordItem.java new file mode 100644 index 0000000..cabb21c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RecordItem.java @@ -0,0 +1,143 @@ +package com.yfd.monitor.gdw2019.bean; + + +import com.yfd.monitor.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import org.jetbrains.annotations.NotNull; + +import java.time.Instant; +import java.time.temporal.TemporalAccessor; + +/** + * @description:设备录像bean + * @date: 2020年5月8日 下午2:06:54 + */ +@Schema(description = "设备录像详情") +public class RecordItem implements Comparable{ + + @Schema(description = "设备编号") + private String deviceId; + + @Schema(description = "名称") + private String name; + + @Schema(description = "文件路径名 (可选)") + private String filePath; + + @Schema(description = "录像文件大小,单位:Byte(可选)") + private String fileSize; + + @Schema(description = "录像地址(可选)") + private String address; + + @Schema(description = "录像开始时间(可选)") + private String startTime; + + @Schema(description = "录像结束时间(可选)") + private String endTime; + + @Schema(description = "保密属性(必选)缺省为0;0:不涉密,1:涉密") + private int secrecy; + + @Schema(description = "录像产生类型(可选)time或alarm 或 manua") + private String type; + + @Schema(description = "录像触发者ID(可选)") + private String recorderId; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public int getSecrecy() { + return secrecy; + } + + public void setSecrecy(int secrecy) { + this.secrecy = secrecy; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getRecorderId() { + return recorderId; + } + + public void setRecorderId(String recorderId) { + this.recorderId = recorderId; + } + + public String getFileSize() { + return fileSize; + } + + public void setFileSize(String fileSize) { + this.fileSize = fileSize; + } + + @Override + public int compareTo(@NotNull RecordItem recordItem) { + TemporalAccessor startTimeNow = DateUtil.formatter.parse(startTime); + TemporalAccessor startTimeParam = DateUtil.formatter.parse(recordItem.getStartTime()); + Instant startTimeParamInstant = Instant.from(startTimeParam); + Instant startTimeNowInstant = Instant.from(startTimeNow); + if (startTimeNowInstant.equals(startTimeParamInstant)) { + return 0; + }else if (Instant.from(startTimeParam).isAfter(Instant.from(startTimeNow)) ) { + return -1; + }else { + return 1; + } + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RemoteAddressInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RemoteAddressInfo.java new file mode 100644 index 0000000..e23d44b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/RemoteAddressInfo.java @@ -0,0 +1,27 @@ +package com.yfd.monitor.gdw2019.bean; + +public class RemoteAddressInfo { + private String ip; + private int port; + + public RemoteAddressInfo(String ip, int port) { + this.ip = ip; + this.port = port; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SDPInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SDPInfo.java new file mode 100644 index 0000000..3824220 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SDPInfo.java @@ -0,0 +1,14 @@ +package com.yfd.monitor.gdw2019.bean; + +import javax.sdp.SessionDescription; + +public class SDPInfo { + private byte[] source; + private SessionDescription sdpSource; + private String sessionName; + private Long startTime; + private Long stopTime; + private String username; + private String address; + private String ssrc; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SendRtpItem.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SendRtpItem.java new file mode 100644 index 0000000..1178c3d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SendRtpItem.java @@ -0,0 +1,295 @@ +package com.yfd.monitor.gdw2019.bean; + +public class SendRtpItem { + + /** + * 推流ip + */ + private String ip; + + /** + * 推流端口 + */ + private int port; + + /** + * 推流标识 + */ + private String ssrc; + + /** + * 平台id + */ + private String platformId; + + /** + * 对应设备id + */ + private String deviceId; + + /** + * 直播流的应用名 + */ + private String app; + + /** + * 通道id + */ + private String channelId; + + /** + * 推流状态 + * 0 等待设备推流上来 + * 1 等待上级平台回复ack + * 2 推流中 + */ + private int status = 0; + + + /** + * 设备推流的streamId + */ + private String streamId; + + /** + * 是否为tcp + */ + private boolean tcp; + + /** + * 是否为tcp主动模式 + */ + private boolean tcpActive; + + /** + * 自己推流使用的端口 + */ + private int localPort; + + /** + * 使用的流媒体 + */ + private String mediaServerId; + + /** + * 使用的服务的ID + */ + private String serverId; + + /** + * invite 的 callId + */ + private String CallId; + + /** + * invite 的 fromTag + */ + private String fromTag; + + /** + * invite 的 toTag + */ + private String toTag; + + /** + * 发送时,rtp的pt(uint8_t),不传时默认为96 + */ + private int pt = 96; + + /** + * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; + */ + private boolean usePs = true; + + /** + * 当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0 + */ + private boolean onlyAudio = false; + + /** + * 是否开启rtcp保活 + */ + private boolean rtcp = false; + + + /** + * 播放类型 + */ + private InviteStreamType playType; + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public boolean isTcp() { + return tcp; + } + + public void setTcp(boolean tcp) { + this.tcp = tcp; + } + + public int getLocalPort() { + return localPort; + } + + public void setLocalPort(int localPort) { + this.localPort = localPort; + } + + public boolean isTcpActive() { + return tcpActive; + } + + public void setTcpActive(boolean tcpActive) { + this.tcpActive = tcpActive; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCallId() { + return CallId; + } + + public void setCallId(String callId) { + CallId = callId; + } + + public InviteStreamType getPlayType() { + return playType; + } + + public void setPlayType(InviteStreamType playType) { + this.playType = playType; + } + + public int getPt() { + return pt; + } + + public void setPt(int pt) { + this.pt = pt; + } + + public boolean isUsePs() { + return usePs; + } + + public void setUsePs(boolean usePs) { + this.usePs = usePs; + } + + public boolean isOnlyAudio() { + return onlyAudio; + } + + public void setOnlyAudio(boolean onlyAudio) { + this.onlyAudio = onlyAudio; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public String getFromTag() { + return fromTag; + } + + public void setFromTag(String fromTag) { + this.fromTag = fromTag; + } + + public String getToTag() { + return toTag; + } + + public void setToTag(String toTag) { + this.toTag = toTag; + } + + public boolean isRtcp() { + return rtcp; + } + + public void setRtcp(boolean rtcp) { + this.rtcp = rtcp; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SipMsgInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SipMsgInfo.java new file mode 100644 index 0000000..1b8f599 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SipMsgInfo.java @@ -0,0 +1,56 @@ +package com.yfd.monitor.gdw2019.bean; + +import org.dom4j.Element; + +import javax.sip.RequestEvent; + +public class SipMsgInfo { + private RequestEvent evt; + private Device device; + private ParentPlatform platform; + private Element rootElement; + + public SipMsgInfo(RequestEvent evt, Device device, Element rootElement) { + this.evt = evt; + this.device = device; + this.rootElement = rootElement; + } + + public SipMsgInfo(RequestEvent evt, ParentPlatform platform, Element rootElement) { + this.evt = evt; + this.platform = platform; + this.rootElement = rootElement; + } + + public RequestEvent getEvt() { + return evt; + } + + public void setEvt(RequestEvent evt) { + this.evt = evt; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + public ParentPlatform getPlatform() { + return platform; + } + + public void setPlatform(ParentPlatform platform) { + this.platform = platform; + } + + public Element getRootElement() { + return rootElement; + } + + public void setRootElement(Element rootElement) { + this.rootElement = rootElement; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SipTransactionInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SipTransactionInfo.java new file mode 100644 index 0000000..a7008d8 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SipTransactionInfo.java @@ -0,0 +1,54 @@ +package com.yfd.monitor.gdw2019.bean; + +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; + +public class SipTransactionInfo { + + private String callId; + private String fromTag; + private String toTag; + private String viaBranch; + + public SipTransactionInfo(SIPResponse response) { + this.callId = response.getCallIdHeader().getCallId(); + this.fromTag = response.getFromTag(); + this.toTag = response.getToTag(); + this.viaBranch = response.getTopmostViaHeader().getBranch(); + } + + public SipTransactionInfo() { + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getFromTag() { + return fromTag; + } + + public void setFromTag(String fromTag) { + this.fromTag = fromTag; + } + + public String getToTag() { + return toTag; + } + + public void setToTag(String toTag) { + this.toTag = toTag; + } + + public String getViaBranch() { + return viaBranch; + } + + public void setViaBranch(String viaBranch) { + this.viaBranch = viaBranch; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SsrcTransaction.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SsrcTransaction.java new file mode 100644 index 0000000..5ace995 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SsrcTransaction.java @@ -0,0 +1,81 @@ +package com.yfd.monitor.gdw2019.bean; + +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; + +public class SsrcTransaction { + + private String deviceId; + private String channelId; + private String callId; + private String stream; + private String mediaServerId; + private String ssrc; + + private SipTransactionInfo sipTransactionInfo; + + private VideoStreamSessionManager.SessionType type; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public VideoStreamSessionManager.SessionType getType() { + return type; + } + + public void setType(VideoStreamSessionManager.SessionType type) { + this.type = type; + } + + public SipTransactionInfo getSipTransactionInfo() { + return sipTransactionInfo; + } + + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) { + this.sipTransactionInfo = sipTransactionInfo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubscribeHolder.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubscribeHolder.java new file mode 100644 index 0000000..54df3f5 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubscribeHolder.java @@ -0,0 +1,159 @@ +package com.yfd.monitor.gdw2019.bean; + +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.gdw2019.task.ISubscribeTask; +import com.yfd.monitor.gdw2019.task.impl.MobilePositionSubscribeHandlerTask; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.service.IPlatformService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + + +@Component +public class SubscribeHolder { + + @Autowired + private DynamicTask dynamicTask; + + private final String taskOverduePrefix = "subscribe_overdue_"; + + private static ConcurrentHashMap catalogMap = new ConcurrentHashMap<>(); + private static ConcurrentHashMap mobilePositionMap = new ConcurrentHashMap<>(); + private static ConcurrentHashMap alarmMap = new ConcurrentHashMap<>(); + private static ConcurrentHashMap statusMap = new ConcurrentHashMap<>(); + + public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) { + catalogMap.put(platformId, subscribeInfo); + // 添加订阅到期 + String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; + // 添加任务处理订阅过期 + dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()), + subscribeInfo.getExpires() * 1000); + } + + public SubscribeInfo getCatalogSubscribe(String platformId) { + return catalogMap.get(platformId); + } + + public void removeCatalogSubscribe(String platformId) { + + catalogMap.remove(platformId); + String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; + Runnable runnable = dynamicTask.get(taskOverdueKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + // 添加任务处理订阅过期 + dynamicTask.stop(taskOverdueKey); + } + + public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) { + mobilePositionMap.put(platformId, subscribeInfo); + String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + "MobilePosition_" + platformId; + // 添加任务处理GPS定时推送 + dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(platformId), + subscribeInfo.getGpsInterval() * 1000); + String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; + // 添加任务处理订阅过期 + dynamicTask.startDelay(taskOverdueKey, () -> { + removeMobilePositionSubscribe(subscribeInfo.getId()); + }, + subscribeInfo.getExpires() * 1000); + } + + public SubscribeInfo getMobilePositionSubscribe(String platformId) { + return mobilePositionMap.get(platformId); + } + + public void removeMobilePositionSubscribe(String platformId) { + mobilePositionMap.remove(platformId); + String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + "MobilePosition_" + platformId; + // 结束任务处理GPS定时推送 + dynamicTask.stop(key); + String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; + Runnable runnable = dynamicTask.get(taskOverdueKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + // 添加任务处理订阅过期 + dynamicTask.stop(taskOverdueKey); + } + + public List getAllCatalogSubscribePlatform() { + List platforms = new ArrayList<>(); + if(catalogMap.size() > 0) { + for (String key : catalogMap.keySet()) { + platforms.add(catalogMap.get(key).getId()); + } + } + return platforms; + } + + public void removeAllSubscribe(String platformId) { + removeMobilePositionSubscribe(platformId); + removeCatalogSubscribe(platformId); + removeAlarmSubscribe(platformId); + removeStatusSubscribe(platformId); + } + + public void putAlarmSubscribe(String platformId, SubscribeInfo subscribeInfo) { + alarmMap.put(platformId, subscribeInfo); + // 添加订阅到期 + String taskOverdueKey = taskOverduePrefix + "alarm_" + platformId; + // 添加任务处理订阅过期 + dynamicTask.startDelay(taskOverdueKey, () -> removeAlarmSubscribe(subscribeInfo.getId()), + subscribeInfo.getExpires() * 1000); + } + + public SubscribeInfo getAlarmSubscribe(String platformId) { + return alarmMap.get(platformId); + } + + + + public void removeAlarmSubscribe(String platformId) { + alarmMap.remove(platformId); + String taskOverdueKey = taskOverduePrefix + "alarm_" + platformId; + Runnable runnable = dynamicTask.get(taskOverdueKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + // 添加任务处理订阅过期 + dynamicTask.stop(taskOverdueKey); + } + + public void putStatusSubscribe(String platformId, SubscribeInfo subscribeInfo) { + statusMap.put(platformId, subscribeInfo); + // 添加订阅到期 + String taskOverdueKey = taskOverduePrefix + "status_" + platformId; + // 添加任务处理订阅过期 + dynamicTask.startDelay(taskOverdueKey, () -> removeStatusSubscribe(subscribeInfo.getId()), + subscribeInfo.getExpires() * 1000); + } + + public SubscribeInfo getStatusSubscribe(String platformId) { + return statusMap.get(platformId); + } + + public void removeStatusSubscribe(String platformId) { + statusMap.remove(platformId); + String taskOverdueKey = taskOverduePrefix + "status_" + platformId; + Runnable runnable = dynamicTask.get(taskOverdueKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + // 添加任务处理订阅过期 + dynamicTask.stop(taskOverdueKey); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubscribeInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubscribeInfo.java new file mode 100644 index 0000000..ae4d824 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubscribeInfo.java @@ -0,0 +1,153 @@ +package com.yfd.monitor.gdw2019.bean; + +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; + +import javax.sip.header.*; + +public class SubscribeInfo { + + + public SubscribeInfo(SIPRequest request, String id) { + this.id = id; + this.request = request; + this.expires = request.getExpires().getExpires(); + EventHeader eventHeader = (EventHeader)request.getHeader(EventHeader.NAME); + this.eventId = eventHeader.getEventId(); + this.eventType = eventHeader.getEventType(); + + } + + public SubscribeInfo() { + } + + private String id; + + private SIPRequest request; + private int expires; + private String eventId; + + private String eventType; + private SIPResponse response; + + /** + * 以下为可选字段 + * @return + */ + private String sn; + + private int gpsInterval; + + //订阅的项目 + private String items; + /** + * 模拟的FromTag + */ + private String simulatedFromTag; + + /** + * 模拟的ToTag + */ + private String simulatedToTag; + + /** + * 模拟的CallID + */ + private String simulatedCallId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public SIPRequest getRequest() { + return request; + } + + public void setRequest(SIPRequest request) { + this.request = request; + } + + public int getExpires() { + return expires; + } + + public void setExpires(int expires) { + this.expires = expires; + } + + public String getEventId() { + return eventId; + } + + public void setEventId(String eventId) { + this.eventId = eventId; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public SIPResponse getResponse() { + return response; + } + + public void setResponse(SIPResponse response) { + this.response = response; + } + + public String getSn() { + return sn; + } + + public void setSn(String sn) { + this.sn = sn; + } + + public int getGpsInterval() { + return gpsInterval; + } + + public void setGpsInterval(int gpsInterval) { + this.gpsInterval = gpsInterval; + } + + public String getSimulatedFromTag() { + return simulatedFromTag; + } + + public void setSimulatedFromTag(String simulatedFromTag) { + this.simulatedFromTag = simulatedFromTag; + } + + public String getSimulatedCallId() { + return simulatedCallId; + } + + public void setSimulatedCallId(String simulatedCallId) { + this.simulatedCallId = simulatedCallId; + } + + public String getSimulatedToTag() { + return simulatedToTag; + } + + public void setSimulatedToTag(String simulatedToTag) { + this.simulatedToTag = simulatedToTag; + } + + public String getItems() { + return items; + } + + public void setItems(String items) { + this.items = items; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubstationNode.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubstationNode.java new file mode 100644 index 0000000..1e9b61a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SubstationNode.java @@ -0,0 +1,81 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + + +@Component +@Data +@Schema(description = "变电站边缘节点信息") +public class SubstationNode { + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 心跳周期(秒) + */ + private int keepTimeout; + + /** + * 巡视主机SIP服务IP地址 + */ + @Value("${sip.ip}") + private String serverIp; + + /** + * 巡视主机SIP服务国标编码 + */ + @Value("${sip.id}") + private String serverId; + + /** + * 巡视主机SIP服务国标域 + */ + @Value("${sip.domain}") + private String serverDomain; + + /** + * 巡视主机SIP服务端口 + */ + @Value("${sip.port}") + private int serverPort; + + /** + * 巡视主机SIP密码 + */ + @Value("${sip.password}") + private int serverPassword; + + /** + * 变电站IP + */ + private String stationIp; + + /** + * 变电站节点国标编码 + */ + private String stationGBId; + + /** + * 变电站节点SIP 端口 + */ + private String stationGBPort; + + /** + * 传输协议 + * UDP/TCP + */ + @Value("TCP") + private String transport; + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SyncStatus.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SyncStatus.java new file mode 100644 index 0000000..7e379ab --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/SyncStatus.java @@ -0,0 +1,51 @@ +package com.yfd.monitor.gdw2019.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 摄像机同步状态 +* + */ +@Schema(description = "摄像机同步状态") +public class SyncStatus { + @Schema(description = "总数") + private int total; + @Schema(description = "当前更新多少") + private int current; + @Schema(description = "错误描述") + private String errorMsg; + @Schema(description = "是否同步中") + private boolean syncIng; + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getCurrent() { + return current; + } + + public void setCurrent(int current) { + this.current = current; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public boolean isSyncIng() { + return syncIng; + } + + public void setSyncIng(boolean syncIng) { + this.syncIng = syncIng; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/TreeType.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/TreeType.java new file mode 100644 index 0000000..1343b43 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/TreeType.java @@ -0,0 +1,9 @@ +package com.yfd.monitor.gdw2019.bean; + +/** + * 目录结构类型 + */ +public class TreeType { + public static final String BUSINESS_GROUP = "BusinessGroup"; + public static final String CIVIL_CODE = "CivilCode"; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/WvpSipDate.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/WvpSipDate.java new file mode 100644 index 0000000..ea75157 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/bean/WvpSipDate.java @@ -0,0 +1,149 @@ +package com.yfd.monitor.gdw2019.bean; + +import gov.nist.core.InternalErrorHandler; +import gov.nist.javax.sip.header.SIPDate; + +import java.util.*; + +/** + * 重写jain sip的SIPDate解决与国标时间格式不一致的问题 + */ +public class WvpSipDate extends SIPDate { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private Calendar javaCal; + + public WvpSipDate(long timeMillis) { + this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault()); + Date date = new Date(timeMillis); + this.javaCal.setTime(date); + this.wkday = this.javaCal.get(7); + switch(this.wkday) { + case 1: + this.sipWkDay = "Sun"; + break; + case 2: + this.sipWkDay = "Mon"; + break; + case 3: + this.sipWkDay = "Tue"; + break; + case 4: + this.sipWkDay = "Wed"; + break; + case 5: + this.sipWkDay = "Thu"; + break; + case 6: + this.sipWkDay = "Fri"; + break; + case 7: + this.sipWkDay = "Sat"; + break; + default: + InternalErrorHandler.handleException("No date map for wkday " + this.wkday); + } + + this.day = this.javaCal.get(5); + this.month = this.javaCal.get(2); + switch(this.month) { + case 0: + this.sipMonth = "Jan"; + break; + case 1: + this.sipMonth = "Feb"; + break; + case 2: + this.sipMonth = "Mar"; + break; + case 3: + this.sipMonth = "Apr"; + break; + case 4: + this.sipMonth = "May"; + break; + case 5: + this.sipMonth = "Jun"; + break; + case 6: + this.sipMonth = "Jul"; + break; + case 7: + this.sipMonth = "Aug"; + break; + case 8: + this.sipMonth = "Sep"; + break; + case 9: + this.sipMonth = "Oct"; + break; + case 10: + this.sipMonth = "Nov"; + break; + case 11: + this.sipMonth = "Dec"; + break; + default: + InternalErrorHandler.handleException("No date map for month " + this.month); + } + + this.year = this.javaCal.get(1); + this.hour = this.javaCal.get(11); + this.minute = this.javaCal.get(12); + this.second = this.javaCal.get(13); + } + + @Override + public StringBuilder encode(StringBuilder var1) { + String var2; + if (this.month < 9) { + var2 = "0" + (this.month + 1); + } else { + var2 = "" + (this.month + 1); + } + + String var3; + if (this.day < 10) { + var3 = "0" + this.day; + } else { + var3 = "" + this.day; + } + + String var4; + if (this.hour < 10) { + var4 = "0" + this.hour; + } else { + var4 = "" + this.hour; + } + + String var5; + if (this.minute < 10) { + var5 = "0" + this.minute; + } else { + var5 = "" + this.minute; + } + + String var6; + if (this.second < 10) { + var6 = "0" + this.second; + } else { + var6 = "" + this.second; + } + + int var8 = this.javaCal.get(14); + String var7; + if (var8 < 10) { + var7 = "00" + var8; + } else if (var8 < 100) { + var7 = "0" + var8; + } else { + var7 = "" + var8; + } + + return var1.append(this.year).append("-").append(var2).append("-").append(var3).append("T").append(var4).append(":").append(var5).append(":").append(var6).append(".").append(var7); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/DefaultProperties.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/DefaultProperties.java new file mode 100644 index 0000000..be2effb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/DefaultProperties.java @@ -0,0 +1,59 @@ +package com.yfd.monitor.gdw2019.conf; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.cmd.AlarmNotifyMessageHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Properties; + +/** + * 获取sip默认配置 +* + */ +public class DefaultProperties { + + public static Properties getProperties(String ip, boolean sipLog) { + Properties properties = new Properties(); + properties.setProperty("javax.sip.STACK_NAME", "gdw2019_SIP"); + properties.setProperty("javax.sip.IP_ADDRESS", ip); + // 关闭自动会话 + properties.setProperty("javax.sip.AUTOMATIC_DIALOG_SUPPORT", "off"); + /** + * 完整配置参考 gov.nist.javax.sip.SipStackImpl,需要下载源码 + * gov/nist/javax/sip/SipStackImpl.class + * sip消息的解析在 gov.nist.javax.sip.stack.UDPMessageChannel的processIncomingDataPacket方法 + */ + +// * gov/nist/javax/sip/SipStackImpl.class + // 接收所有notify请求,即使没有订阅 + properties.setProperty("gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY", "true"); + properties.setProperty("gov.nist.javax.sip.AUTOMATIC_DIALOG_ERROR_HANDLING", "false"); + properties.setProperty("gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED", "true"); + // 为_NULL _对话框传递_终止的_事件 + properties.setProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", "true"); + // 会话清理策略 + properties.setProperty("gov.nist.javax.sip.RELEASE_REFERENCES_STRATEGY", "Normal"); + // 处理由该服务器处理的基于底层TCP的保持生存超时 + properties.setProperty("gov.nist.javax.sip.RELIABLE_CONNECTION_KEEP_ALIVE_TIMEOUT", "60"); + // 获取实际内容长度,不使用header中的长度信息 + properties.setProperty("gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "true"); + // 线程可重入 + properties.setProperty("gov.nist.javax.sip.REENTRANT_LISTENER", "true"); + // 定义应用程序打算多久审计一次 SIP 堆栈,了解其内部线程的健康状况(该属性指定连续审计之间的时间(以毫秒为单位)) + properties.setProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS", "30000"); + + /** + * sip_server_log.log 和 sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE + */ + Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); + if (sipLog) { + properties.setProperty("gov.nist.javax.sip.STACK_LOGGER", "com.yfd.monitor.gdw2019.conf.StackLoggerImpl"); + properties.setProperty("gov.nist.javax.sip.SERVER_LOGGER", "com.yfd.monitor.gdw2019.conf.ServerLoggerImpl"); + properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "true"); + logger.info("[SIP日志]已开启"); + }else { + logger.info("[SIP日志]已关闭"); + } + return properties; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/ServerLoggerImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/ServerLoggerImpl.java new file mode 100644 index 0000000..fbdbee7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/ServerLoggerImpl.java @@ -0,0 +1,92 @@ +package com.yfd.monitor.gdw2019.conf; + +import gov.nist.core.ServerLogger; +import gov.nist.core.StackLogger; +import gov.nist.javax.sip.message.SIPMessage; +import gov.nist.javax.sip.stack.SIPTransactionStack; + +import javax.sip.SipStack; +import java.util.Properties; + +public class ServerLoggerImpl implements ServerLogger { + + private boolean showLog = true; + + private SIPTransactionStack sipStack; + + protected StackLogger stackLogger; + + @Override + public void closeLogFile() { + + } + + @Override + public void logMessage(SIPMessage message, String from, String to, boolean sender, long time) { + if (!showLog) { + return; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(!sender? "发送:目标--->" + from:"接收:来自--->" + to) + .append("\r\n") + .append(message); + this.stackLogger.logInfo(stringBuilder.toString()); + + } + + @Override + public void logMessage(SIPMessage message, String from, String to, String status, boolean sender, long time) { + if (!showLog) { + return; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(!sender? "发送: 目标->" + from :"接收:来自->" + to) + .append("\r\n") + .append(message); + this.stackLogger.logInfo(stringBuilder.toString()); + } + + @Override + public void logMessage(SIPMessage message, String from, String to, String status, boolean sender) { + if (!showLog) { + return; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(!sender? "发送: 目标->" + from :"接收:来自->" + to) + .append("\r\n") + .append(message); + this.stackLogger.logInfo(stringBuilder.toString()); + } + + @Override + public void logException(Exception ex) { + if (!showLog) { + return; + } + this.stackLogger.logException(ex); + } + + @Override + public void setStackProperties(Properties stackProperties) { + if (!showLog) { + return; + } + String TRACE_LEVEL = stackProperties.getProperty("gov.nist.javax.sip.TRACE_LEVEL"); + if (TRACE_LEVEL != null) { + showLog = true; + } + } + + @Override + public void setSipStack(SipStack sipStack) { + if (!showLog) { + return; + } + if(sipStack instanceof SIPTransactionStack) { + this.sipStack = (SIPTransactionStack)sipStack; + this.stackLogger = this.sipStack.getStackLogger(); + } + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/StackLoggerImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/StackLoggerImpl.java new file mode 100644 index 0000000..f204a42 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/conf/StackLoggerImpl.java @@ -0,0 +1,109 @@ +package com.yfd.monitor.gdw2019.conf; + +import gov.nist.core.StackLogger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.Properties; + +@Component +public class StackLoggerImpl implements StackLogger { + + private final static Logger logger = LoggerFactory.getLogger(StackLoggerImpl.class); + + @Override + public void logStackTrace() { + + } + + @Override + public void logStackTrace(int traceLevel) { + logger.info("traceLevel: " + traceLevel); + } + + @Override + public int getLineCount() { + return 0; + } + + @Override + public void logException(Throwable ex) { + + } + + @Override + public void logDebug(String message) { +// logger.debug(message); + } + + @Override + public void logDebug(String message, Exception ex) { +// logger.debug(message); + } + + @Override + public void logTrace(String message) { + logger.trace(message); + } + + @Override + public void logFatalError(String message) { +// logger.error(message); + } + + @Override + public void logError(String message) { +// logger.error(message); + } + + @Override + public boolean isLoggingEnabled() { + return true; + } + + @Override + public boolean isLoggingEnabled(int logLevel) { + return true; + } + + @Override + public void logError(String message, Exception ex) { +// logger.error(message); + } + + @Override + public void logWarning(String message) { + logger.warn(message); + } + + @Override + public void logInfo(String message) { + logger.info(message); + } + + @Override + public void disableLogging() { + + } + + @Override + public void enableLogging() { + + } + + @Override + public void setBuildTimeStamp(String buildTimeStamp) { + + } + + @Override + public void setStackProperties(Properties stackProperties) { + + } + + @Override + public String getLoggerName() { + return null; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/EventPublisher.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/EventPublisher.java new file mode 100644 index 0000000..8bbfd70 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/EventPublisher.java @@ -0,0 +1,126 @@ +package com.yfd.monitor.gdw2019.event; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.device.RequestTimeoutEvent; +import com.yfd.monitor.gdw2019.event.record.RecordEndEvent; +import com.yfd.monitor.gdw2019.event.status.StatusEvent; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.media.zlm.event.ZLMOfflineEvent; +import com.yfd.monitor.media.zlm.event.ZLMOnlineEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +import com.yfd.monitor.gdw2019.event.alarm.AlarmEvent; + +import javax.sip.TimeoutEvent; +import java.util.*; + +/** + * @description:Event事件通知推送器,支持推送在线事件、离线事件 +* + * @date: 2020年5月6日 上午11:30:50 + */ +@Component +public class EventPublisher { + + @Autowired + private ApplicationEventPublisher applicationEventPublisher; + + /** + * 设备报警事件 + * @param deviceAlarm + */ + public void deviceAlarmEventPublish(DeviceAlarm deviceAlarm) { + AlarmEvent alarmEvent = new AlarmEvent(this); + alarmEvent.setAlarmInfo(deviceAlarm); + applicationEventPublisher.publishEvent(alarmEvent); + } + + /** + * 设备状态通知事件 + * @param deviceStatus + */ + public void deviceStatusEventPublish(Map deviceStatus) { + StatusEvent statusEvent = new StatusEvent(this); + statusEvent.setDevicestatus(deviceStatus); + applicationEventPublisher.publishEvent(statusEvent); + } + + public void zlmOfflineEventPublish(String mediaServerId){ + ZLMOfflineEvent outEvent = new ZLMOfflineEvent(this); + outEvent.setMediaServerId(mediaServerId); + applicationEventPublisher.publishEvent(outEvent); + } + + public void zlmOnlineEventPublish(String mediaServerId) { + ZLMOnlineEvent outEvent = new ZLMOnlineEvent(this); + outEvent.setMediaServerId(mediaServerId); + applicationEventPublisher.publishEvent(outEvent); + } + + + public void catalogEventPublish(String platformId, DeviceChannel deviceChannel, String type) { + List deviceChannelList = new ArrayList<>(); + deviceChannelList.add(deviceChannel); + catalogEventPublish(platformId, deviceChannelList, type); + } + + + public void requestTimeOut(TimeoutEvent timeoutEvent) { + RequestTimeoutEvent requestTimeoutEvent = new RequestTimeoutEvent(this); + requestTimeoutEvent.setTimeoutEvent(timeoutEvent); + applicationEventPublisher.publishEvent(requestTimeoutEvent); + } + + + /** + * + * @param platformId + * @param deviceChannels + * @param type + */ + public void catalogEventPublish(String platformId, List deviceChannels, String type) { + CatalogEvent outEvent = new CatalogEvent(this); + List channels = new ArrayList<>(); + if (deviceChannels.size() > 1) { + // 数据去重 + Set gbIdSet = new HashSet<>(); + for (DeviceChannel deviceChannel : deviceChannels) { + if (!gbIdSet.contains(deviceChannel.getChannelId())) { + gbIdSet.add(deviceChannel.getChannelId()); + channels.add(deviceChannel); + } + } + }else { + channels = deviceChannels; + } + outEvent.setDeviceChannels(channels); + outEvent.setType(type); + outEvent.setPlatformId(platformId); + applicationEventPublisher.publishEvent(outEvent); + } + + + public void catalogEventPublishForStream(String platformId, List gbStreams, String type) { + CatalogEvent outEvent = new CatalogEvent(this); + outEvent.setGbStreams(gbStreams); + outEvent.setType(type); + outEvent.setPlatformId(platformId); + applicationEventPublisher.publishEvent(outEvent); + } + + + public void catalogEventPublishForStream(String platformId, GbStream gbStream, String type) { + List gbStreamList = new ArrayList<>(); + gbStreamList.add(gbStream); + catalogEventPublishForStream(platformId, gbStreamList, type); + } + + public void recordEndEventPush(RecordInfo recordInfo) { + RecordEndEvent outEvent = new RecordEndEvent(this); + outEvent.setRecordInfo(recordInfo); + applicationEventPublisher.publishEvent(outEvent); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/SipSubscribe.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/SipSubscribe.java new file mode 100644 index 0000000..df929ee --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/SipSubscribe.java @@ -0,0 +1,178 @@ +package com.yfd.monitor.gdw2019.event; + +import com.yfd.monitor.gdw2019.bean.DeviceNotFoundEvent; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.sip.DialogTerminatedEvent; +import javax.sip.ResponseEvent; +import javax.sip.TimeoutEvent; +import javax.sip.TransactionTerminatedEvent; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.time.Instant; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + + +@Component +public class SipSubscribe { + + private final Logger logger = LoggerFactory.getLogger(SipSubscribe.class); + + private Map errorSubscribes = new ConcurrentHashMap<>(); + + private Map okSubscribes = new ConcurrentHashMap<>(); + + private Map okTimeSubscribes = new ConcurrentHashMap<>(); + + private Map errorTimeSubscribes = new ConcurrentHashMap<>(); + + // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 + // @Scheduled(fixedRate= 100 * 60 * 60 ) + @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 + public void execute(){ + logger.info("[定时任务] 清理过期的SIP订阅信息"); + + Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); + + for (String key : okTimeSubscribes.keySet()) { + if (okTimeSubscribes.get(key).isBefore(instant)){ + okSubscribes.remove(key); + okTimeSubscribes.remove(key); + } + } + for (String key : errorTimeSubscribes.keySet()) { + if (errorTimeSubscribes.get(key).isBefore(instant)){ + errorSubscribes.remove(key); + errorTimeSubscribes.remove(key); + } + } + logger.debug("okTimeSubscribes.size:{}",okTimeSubscribes.size()); + logger.debug("okSubscribes.size:{}",okSubscribes.size()); + logger.debug("errorTimeSubscribes.size:{}",errorTimeSubscribes.size()); + logger.debug("errorSubscribes.size:{}",errorSubscribes.size()); + } + + public interface Event { void response(EventResult eventResult) ; + } + + /** + * + */ + public enum EventResultType{ + // 超时 + timeout, + // 回复 + response, + // 事务已结束 + transactionTerminated, + // 会话已结束 + dialogTerminated, + // 设备未找到 + deviceNotFoundEvent, + // 设备未找到 + cmdSendFailEvent + } + + public static class EventResult{ + public int statusCode; + public EventResultType type; + public String msg; + public String callId; + public EventObject event; + + public EventResult() { + } + + public EventResult(EventObject event) { + this.event = event; + if (event instanceof ResponseEvent) { + ResponseEvent responseEvent = (ResponseEvent)event; + Response response = responseEvent.getResponse(); + this.type = EventResultType.response; + if (response != null) { + this.msg = response.getReasonPhrase(); + this.statusCode = response.getStatusCode(); + } + this.callId = ((CallIdHeader)response.getHeader(CallIdHeader.NAME)).getCallId(); + + }else if (event instanceof TimeoutEvent) { + TimeoutEvent timeoutEvent = (TimeoutEvent)event; + this.type = EventResultType.timeout; + this.msg = "消息超时未回复"; + this.statusCode = -1024; + if (timeoutEvent.isServerTransaction()) { + this.callId = ((SIPRequest)timeoutEvent.getServerTransaction().getRequest()).getCallIdHeader().getCallId(); + }else { + this.callId = ((SIPRequest)timeoutEvent.getClientTransaction().getRequest()).getCallIdHeader().getCallId(); + } + }else if (event instanceof TransactionTerminatedEvent) { + TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event; + this.type = EventResultType.transactionTerminated; + this.msg = "事务已结束"; + this.statusCode = -1024; + if (transactionTerminatedEvent.isServerTransaction()) { + this.callId = ((SIPRequest)transactionTerminatedEvent.getServerTransaction().getRequest()).getCallIdHeader().getCallId(); + }else { + this.callId = ((SIPRequest)transactionTerminatedEvent.getClientTransaction().getRequest()).getCallIdHeader().getCallId(); + } + }else if (event instanceof DialogTerminatedEvent) { + DialogTerminatedEvent dialogTerminatedEvent = (DialogTerminatedEvent)event; + this.type = EventResultType.dialogTerminated; + this.msg = "会话已结束"; + this.statusCode = -1024; + this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId(); + }else if (event instanceof DeviceNotFoundEvent) { + this.type = EventResultType.deviceNotFoundEvent; + this.msg = "设备未找到"; + this.statusCode = -1024; + this.callId = ((DeviceNotFoundEvent) event).getCallId(); + } + } + } + + public void addErrorSubscribe(String key, SipSubscribe.Event event) { + errorSubscribes.put(key, event); + errorTimeSubscribes.put(key, Instant.now()); + } + + public void addOkSubscribe(String key, SipSubscribe.Event event) { + okSubscribes.put(key, event); + okTimeSubscribes.put(key, Instant.now()); + } + + public SipSubscribe.Event getErrorSubscribe(String key) { + return errorSubscribes.get(key); + } + + public void removeErrorSubscribe(String key) { + if(key == null){ + return; + } + errorSubscribes.remove(key); + errorTimeSubscribes.remove(key); + } + + public SipSubscribe.Event getOkSubscribe(String key) { + return okSubscribes.get(key); + } + + public void removeOkSubscribe(String key) { + if(key == null){ + return; + } + okSubscribes.remove(key); + okTimeSubscribes.remove(key); + } + public int getErrorSubscribesSize(){ + return errorSubscribes.size(); + } + public int getOkSubscribesSize(){ + return okSubscribes.size(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/alarm/AlarmEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/alarm/AlarmEvent.java new file mode 100644 index 0000000..1a5b701 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/alarm/AlarmEvent.java @@ -0,0 +1,30 @@ +package com.yfd.monitor.gdw2019.event.alarm; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import org.springframework.context.ApplicationEvent; + +/** + * @description: 报警事件 + * @data: 2021-01-20 + */ + +public class AlarmEvent extends ApplicationEvent { + /** + * + */ + private static final long serialVersionUID = 1L; + + public AlarmEvent(Object source) { + super(source); + } + + private DeviceAlarm deviceAlarm; + + public DeviceAlarm getAlarmInfo() { + return deviceAlarm; + } + + public void setAlarmInfo(DeviceAlarm deviceAlarm) { + this.deviceAlarm = deviceAlarm; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/alarm/AlarmEventListener.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/alarm/AlarmEventListener.java new file mode 100644 index 0000000..408c9ac --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/alarm/AlarmEventListener.java @@ -0,0 +1,209 @@ +package com.yfd.monitor.gdw2019.event.alarm; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.*; + +/** + * @description: 报警事件监听 + * @data: 2021-01-20 + */ + +@Component +public class AlarmEventListener implements ApplicationListener { + + private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); + + private static Map sseEmitters = new Hashtable<>(); + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private IVideoManagerStorage storager; + @Autowired + private ISIPCommanderForPlatform cmdforplatform; + + public void addSseEmitters(String browserId, SseEmitter sseEmitter) { + sseEmitters.put(browserId, sseEmitter); + } + + @Override + public void onApplicationEvent(AlarmEvent event) { + // 获取所用订阅 + List parentPlatforms = storager.queryEnableParentPlatformList(true); + if (parentPlatforms.size() == 0) return; + ParentPlatform parentPlatform = parentPlatforms.get(0); + SubscribeInfo subscribeinfo = subscribeHolder.getAlarmSubscribe(parentPlatform.getServerGBId()); + if (subscribeinfo == null) { + return; + } + DeviceAlarm deviceAlarm = event.getAlarmInfo(); + String items = subscribeinfo.getItems(); + JSONArray jsonArray = JSON.parseArray(items); + List> alarmitems = new ArrayList<>(); + for (Object o : jsonArray) { + JSONObject jobj = (JSONObject) o; + int alarmType = Integer.parseInt(jobj.getString("Type")); + // 截取每位字节 + int videoLoss = (alarmType & 0x01) >> 0; + int motionDetection = (alarmType & 0x02) >> 1; + int videoMask = (alarmType & 0x04) >> 2; + int highTemperature = (alarmType & 0x100) >> 8; + int lowTemperature = (alarmType & 0x200) >> 9; + int fanFailure = (alarmType & 0x400) >> 10; + int diskFailure = (alarmType & 0x800) >> 11; + int statusEvent = (alarmType & 0x10000) >> 16; + if (jobj.getString("Code").equals(deviceAlarm.getDeviceId())) {//订阅了设备 + if (deviceAlarm.getAlarmMethod().equals("2")) {//报警设备报警 + switch (deviceAlarm.getAlarmType()) { + case "1"://1-视频丢失报警; 第 0 位:视频丢失告警 + if (videoLoss == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + case "4"://4-设备高温报警; 第 8 位:设备高温告警 + if (highTemperature == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + case "5"://5-设备低温报警。 第 9 位:设备低温告警 + if (lowTemperature == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + + } + } + if (deviceAlarm.getAlarmMethod().equals("5")) {//5为视频报警 + switch (deviceAlarm.getAlarmType()) { + case "6"://6-入侵检测报警; 第 1 位:移动侦测告警 + if (motionDetection == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + case "11"://11-视频异常检测报警; 第 2 位:视频遮挡告警 + if (videoMask == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + case "12"://12-快速移动报警 第 1 位:移动侦测告警 + if (motionDetection == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + } + } + if (deviceAlarm.getAlarmMethod().equals("6")) {//报警设备报警 + switch (deviceAlarm.getAlarmType()) { + case "1"://1-存储设备磁盘故障报警; 第 11 位:磁盘故障告警 + if (diskFailure == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + case "2"://2-存储设备风扇故障报警。 第 10 位:风扇故障告警 + if (fanFailure == 1) { + Map map = new HashMap<>(); + map.put("Code", jobj.getString("Code")); + map.put("Name", deviceAlarm.getAlarmDescription()); + map.put("Type", jobj.getString("Type")); + map.put("StartTime", deviceAlarm.getAlarmTime()); + alarmitems.add(map); + } + break; + + + } + } + } + } + if (alarmitems.size() > 0) { + try { + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(subscribeinfo.getResponse()); + cmdforplatform.sendAlarmNotice(parentPlatform, alarmitems, sipTransactionInfo); + } catch (ParseException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (SipException e) { + e.printStackTrace(); + } + + } + + } + //2024-02-12 注释,将sse推送修改为向上级发送告警通知 +// if (logger.isDebugEnabled()) { +// logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", " +// + event.getAlarmInfo().getAlarmDescription()); +// } +// String msg = "设备编码: " + event.getAlarmInfo().getDeviceId() + "" +// + "
报警描述: " + event.getAlarmInfo().getAlarmDescription() + "" +// + "
报警时间: " + event.getAlarmInfo().getAlarmTime() + "" +// + "
报警位置: " + event.getAlarmInfo().getLongitude() + "" +// + ", " + event.getAlarmInfo().getLatitude() + ""; +// +// for (Iterator> it = sseEmitters.entrySet().iterator(); it.hasNext();) { +// Map.Entry emitter = it.next(); +// logger.info("推送到SSE连接,浏览器ID: " + emitter.getKey()); +// try { +// emitter.getValue().send(msg); +// } catch (IOException | IllegalStateException e) { +// if (logger.isDebugEnabled()) { +// logger.debug("SSE连接已关闭"); +// } +// // 移除已关闭的连接 +// it.remove(); +// } +// } +// } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/device/RequestTimeoutEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/device/RequestTimeoutEvent.java new file mode 100644 index 0000000..e85fa6d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/device/RequestTimeoutEvent.java @@ -0,0 +1,23 @@ +package com.yfd.monitor.gdw2019.event.device; + +import org.springframework.context.ApplicationEvent; + +import javax.sip.TimeoutEvent; + + +public class RequestTimeoutEvent extends ApplicationEvent { + public RequestTimeoutEvent(Object source) { + super(source); + } + + + private TimeoutEvent timeoutEvent; + + public TimeoutEvent getTimeoutEvent() { + return timeoutEvent; + } + + public void setTimeoutEvent(TimeoutEvent timeoutEvent) { + this.timeoutEvent = timeoutEvent; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/device/RequestTimeoutEventImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/device/RequestTimeoutEventImpl.java new file mode 100644 index 0000000..8200852 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/device/RequestTimeoutEventImpl.java @@ -0,0 +1,37 @@ +package com.yfd.monitor.gdw2019.event.device; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.service.IDeviceService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import javax.sip.ClientTransaction; +import javax.sip.address.SipURI; +import javax.sip.message.Request; + + +@Component +public class RequestTimeoutEventImpl implements ApplicationListener { + + @Autowired + private IDeviceService deviceService; + + @Override + public void onApplicationEvent(RequestTimeoutEvent event) { + ClientTransaction clientTransaction = event.getTimeoutEvent().getClientTransaction(); + if (clientTransaction != null) { + Request request = clientTransaction.getRequest(); + if (request != null) { + String host = ((SipURI) request.getRequestURI()).getHost(); + int port = ((SipURI) request.getRequestURI()).getPort(); + Device device = deviceService.getDeviceByHostAndPort(host, port); + if (device == null) { + return; + } + deviceService.offline(device.getDeviceId(), "等待消息超时"); + } + + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/record/RecordEndEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/record/RecordEndEvent.java new file mode 100644 index 0000000..292a8f0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/record/RecordEndEvent.java @@ -0,0 +1,31 @@ +package com.yfd.monitor.gdw2019.event.record; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import com.yfd.monitor.gdw2019.bean.RecordInfo; +import org.springframework.context.ApplicationEvent; + +/** + * @description: 录像查询结束时间 + * @data: 2022-02-23 + */ + +public class RecordEndEvent extends ApplicationEvent { + /** + * + */ + private static final long serialVersionUID = 1L; + + public RecordEndEvent(Object source) { + super(source); + } + + private RecordInfo recordInfo; + + public RecordInfo getRecordInfo() { + return recordInfo; + } + + public void setRecordInfo(RecordInfo recordInfo) { + this.recordInfo = recordInfo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/record/RecordEndEventListener.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/record/RecordEndEventListener.java new file mode 100644 index 0000000..140fc28 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/record/RecordEndEventListener.java @@ -0,0 +1,66 @@ +package com.yfd.monitor.gdw2019.event.record; + +import com.yfd.monitor.gdw2019.bean.RecordInfo; +import com.yfd.monitor.utils.redis.RedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @description: 录像查询结束事件 + * @data: 2022-02-23 + */ + +@Component +public class RecordEndEventListener implements ApplicationListener { + + private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class); + + private Map handlerMap = new ConcurrentHashMap<>(); + public interface RecordEndEventHandler{ + void handler(RecordInfo recordInfo); + } + + @Override + public void onApplicationEvent(RecordEndEvent event) { + String deviceId = event.getRecordInfo().getDeviceId(); + String channelId = event.getRecordInfo().getChannelId(); + int count = event.getRecordInfo().getCount(); + int sumNum = event.getRecordInfo().getSumNum(); +// logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}/{}条", event.getRecordInfo().getDeviceId(), +// event.getRecordInfo().getChannelId(), count,sumNum); + if (handlerMap.size() > 0) { + RecordEndEventHandler handler = handlerMap.get(deviceId + channelId); + if (handler !=null){ + handler.handler(event.getRecordInfo()); + if (count ==sumNum){ + handlerMap.remove(deviceId + channelId); + } + } + } + } + + /** + * 添加 + * @param device + * @param channelId + * @param recordEndEventHandler + */ + public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) { + handlerMap.put(device + channelId, recordEndEventHandler); + } + /** + * 添加 + * @param device + * @param channelId + */ + public void delEndEventHandler(String device, String channelId) { + handlerMap.remove(device + channelId); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/status/StatusEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/status/StatusEvent.java new file mode 100644 index 0000000..b163f16 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/status/StatusEvent.java @@ -0,0 +1,33 @@ +package com.yfd.monitor.gdw2019.event.status; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import org.springframework.context.ApplicationEvent; + +import java.util.Map; + +/** + * @description: 设备状态事件 + * @data: 2021-01-20 + */ + +public class StatusEvent extends ApplicationEvent { + /** + * + */ + private static final long serialVersionUID = 1L; + + public StatusEvent(Object source) { + super(source); + } + + private Map devicestatus; + + public Map getDevicestatus() { + return devicestatus; + } + + public void setDevicestatus(Map devicestatus) { + this.devicestatus = devicestatus; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/status/StatusEventListener.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/status/StatusEventListener.java new file mode 100644 index 0000000..239f567 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/status/StatusEventListener.java @@ -0,0 +1,81 @@ +package com.yfd.monitor.gdw2019.event.status; + +import cn.hutool.core.util.ObjUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.*; + +/** + * @description: 设备状态事件监听 + * @data: 2021-01-20 + */ + +@Component +public class StatusEventListener implements ApplicationListener { + + private final static Logger logger = LoggerFactory.getLogger(StatusEventListener.class); + + private static Map sseEmitters = new Hashtable<>(); + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private IVideoManagerStorage storager; + @Autowired + private ISIPCommanderForPlatform cmdforplatform; + + public void addSseEmitters(String browserId, SseEmitter sseEmitter) { + sseEmitters.put(browserId, sseEmitter); + } + + @Override + public void onApplicationEvent(StatusEvent event) { + // 获取所用订阅 + List parentPlatforms = storager.queryEnableParentPlatformList(true); + if (parentPlatforms.size()==0) return; + ParentPlatform parentPlatform=parentPlatforms.get(0); + SubscribeInfo subscribeinfo = subscribeHolder.getStatusSubscribe(parentPlatform.getServerGBId()); + + Map devicestatus=event.getDevicestatus(); + + List> statusitems=new ArrayList<>(); + if(ObjUtil.isNotEmpty(subscribeinfo)){ + String items=subscribeinfo.getItems(); + JSONArray jsonArray= JSON.parseArray(items); + for (Object o : jsonArray) { + JSONObject jobj=(JSONObject) o; + if(jobj.getString("Code").equals(devicestatus.get("Code").toString())) {//订阅了设备 + statusitems.add(devicestatus); + } + } + } + + if(statusitems.size()>0){ + try { + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(subscribeinfo.getResponse()); + cmdforplatform.sendDeviceStatusNotice(parentPlatform,statusitems,sipTransactionInfo); + } catch (ParseException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (SipException e) { + e.printStackTrace(); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/subscribe/catalog/CatalogEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/subscribe/catalog/CatalogEvent.java new file mode 100644 index 0000000..8b052ee --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/subscribe/catalog/CatalogEvent.java @@ -0,0 +1,85 @@ +package com.yfd.monitor.gdw2019.event.subscribe.catalog; + +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.GbStream; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +public class CatalogEvent extends ApplicationEvent { + public CatalogEvent(Object source) { + super(source); + } + + /** + * 上线 + */ + public static final String ON = "ON"; + + /** + * 离线 + */ + public static final String OFF = "OFF"; + + /** + * 视频丢失 + */ + public static final String VLOST = "VLOST"; + + /** + * 故障 + */ + public static final String DEFECT = "DEFECT"; + + /** + * 增加 + */ + public static final String ADD = "ADD"; + + /** + * 删除 + */ + public static final String DEL = "DEL"; + + /** + * 更新 + */ + public static final String UPDATE = "UPDATE"; + + private List deviceChannels; + private List gbStreams; + private String type; + private String platformId; + + public List getDeviceChannels() { + return deviceChannels; + } + + public void setDeviceChannels(List deviceChannels) { + this.deviceChannels = deviceChannels; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public List getGbStreams() { + return gbStreams; + } + + public void setGbStreams(List gbStreams) { + this.gbStreams = gbStreams; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/subscribe/catalog/CatalogEventLister.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/subscribe/catalog/CatalogEventLister.java new file mode 100644 index 0000000..ddc33c1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/event/subscribe/catalog/CatalogEventLister.java @@ -0,0 +1,194 @@ +package com.yfd.monitor.gdw2019.event.subscribe.catalog; + +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.*; + +/** + * catalog事件 + */ +@Component +public class CatalogEventLister implements ApplicationListener { + + private final static Logger logger = LoggerFactory.getLogger(CatalogEventLister.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Override + public void onApplicationEvent(CatalogEvent event) { + SubscribeInfo subscribe = null; + ParentPlatform parentPlatform = null; + + Map> parentPlatformMap = new HashMap<>(); + if (!ObjectUtils.isEmpty(event.getPlatformId())) { + subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId()); + if (subscribe == null) { + return; + } + parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformId()); + if (parentPlatform != null && !parentPlatform.isStatus()) { + return; + } + + }else { + // 获取所用订阅 + List platforms = subscribeHolder.getAllCatalogSubscribePlatform(); + if (event.getDeviceChannels() != null) { + if (platforms.size() > 0) { + for (DeviceChannel deviceChannel : event.getDeviceChannels()) { + List parentPlatformsForGB = storager.queryPlatFormListForGBWithGBId(deviceChannel.getChannelId(), platforms); + parentPlatformMap.put(deviceChannel.getChannelId(), parentPlatformsForGB); + } + } + }else if (event.getGbStreams() != null) { + if (platforms.size() > 0) { + for (GbStream gbStream : event.getGbStreams()) { + if (gbStream == null || ObjectUtils.isEmpty(gbStream.getGbId())) { + continue; + } + List parentPlatformsForGB = storager.queryPlatFormListForStreamWithGBId(gbStream.getApp(),gbStream.getStream(), platforms); + parentPlatformMap.put(gbStream.getGbId(), parentPlatformsForGB); + } + } + } + } + switch (event.getType()) { + case CatalogEvent.ON: + case CatalogEvent.OFF: + case CatalogEvent.DEL: + + if (parentPlatform != null || subscribe != null) { + List deviceChannelList = new ArrayList<>(); + if (event.getDeviceChannels() != null) { + deviceChannelList.addAll(event.getDeviceChannels()); + } + if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ + for (GbStream gbStream : event.getGbStreams()) { + DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform); + deviceChannelList.add(deviceChannelByStream); + } + } + if (deviceChannelList.size() > 0) { + logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); +// try { +// sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null); +// } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | +// IllegalAccessException e) { +// logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); +// } + } + }else if (parentPlatformMap.keySet().size() > 0) { + for (String gbId : parentPlatformMap.keySet()) { + List parentPlatforms = parentPlatformMap.get(gbId); + if (parentPlatforms != null && parentPlatforms.size() > 0) { + for (ParentPlatform platform : parentPlatforms) { + SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); + if (subscribeInfo == null) { + continue; + } + logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); + List deviceChannelList = new ArrayList<>(); + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbId); + deviceChannelList.add(deviceChannel); +// try { +//// sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null); +//// } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | +//// IllegalAccessException e) { +//// logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); +//// } + } + } + } + } + break; + case CatalogEvent.VLOST: + break; + case CatalogEvent.DEFECT: + break; + case CatalogEvent.ADD: + case CatalogEvent.UPDATE: + if (parentPlatform != null || subscribe != null) { + List deviceChannelList = new ArrayList<>(); + if (event.getDeviceChannels() != null) { + deviceChannelList.addAll(event.getDeviceChannels()); + } + if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ + for (GbStream gbStream : event.getGbStreams()) { + deviceChannelList.add( + gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform)); + } + } + if (deviceChannelList.size() > 0) { + logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); +// try { +// sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null); +// } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | +// IllegalAccessException e) { +// logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); +// } + } + }else if (parentPlatformMap.keySet().size() > 0) { + for (String gbId : parentPlatformMap.keySet()) { + List parentPlatforms = parentPlatformMap.get(gbId); + if (parentPlatforms != null && parentPlatforms.size() > 0) { + for (ParentPlatform platform : parentPlatforms) { + SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); + if (subscribeInfo == null) { + continue; + } + logger.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); + List deviceChannelList = new ArrayList<>(); + DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(platform.getServerGBId(), gbId); + deviceChannelList.add(deviceChannel); + GbStream gbStream = storager.queryStreamInParentPlatform(platform.getServerGBId(), gbId); + if(gbStream != null){ + DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), platform); + deviceChannelList.add(deviceChannelByStream); + } +// try { +// sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), platform, deviceChannelList, subscribeInfo, null); +// } catch (InvalidArgumentException | ParseException | NoSuchFieldException | +// SipException | IllegalAccessException e) { +// logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); +// } + } + } + } + } + break; + default: + break; + } + } +} + \ No newline at end of file diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/AudioBroadcastManager.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/AudioBroadcastManager.java new file mode 100644 index 0000000..3057643 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/AudioBroadcastManager.java @@ -0,0 +1,109 @@ +package com.yfd.monitor.gdw2019.session; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.bean.AudioBroadcastCatch; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 语音广播消息管理类 + * @author lin + */ +@Component +public class AudioBroadcastManager { + + private final static Logger logger = LoggerFactory.getLogger(AudioBroadcastManager.class); + + @Autowired + private SipConfig config; + + public static Map data = new ConcurrentHashMap<>(); + + + public void update(AudioBroadcastCatch audioBroadcastCatch) { + if (SipUtils.isFrontEnd(audioBroadcastCatch.getDeviceId())) { + audioBroadcastCatch.setChannelId(audioBroadcastCatch.getDeviceId()); + data.put(audioBroadcastCatch.getDeviceId(), audioBroadcastCatch); + }else { + data.put(audioBroadcastCatch.getDeviceId() + audioBroadcastCatch.getChannelId(), audioBroadcastCatch); + } + } + + public void del(String deviceId, String channelId) { + if (SipUtils.isFrontEnd(deviceId)) { + data.remove(deviceId); + }else { + data.remove(deviceId + channelId); + } + + } + + public void delByDeviceId(String deviceId) { + for (String key : data.keySet()) { + if (key.startsWith(deviceId)) { + data.remove(key); + } + } + } + + public List getAll(){ + Collection values = data.values(); + return new ArrayList<>(values); + } + + + public boolean exit(String deviceId, String channelId) { + for (String key : data.keySet()) { + if (SipUtils.isFrontEnd(deviceId)) { + return key.equals(deviceId); + }else { + return key.equals(deviceId + channelId); + } + } + return false; + } + + public AudioBroadcastCatch get(String deviceId, String channelId) { + AudioBroadcastCatch audioBroadcastCatch; + if (SipUtils.isFrontEnd(deviceId)) { + audioBroadcastCatch = data.get(deviceId); + }else { + audioBroadcastCatch = data.get(deviceId + channelId); + } + if (audioBroadcastCatch == null) { + Stream allAudioBroadcastCatchStreamForDevice = data.values().stream().filter( + audioBroadcastCatchItem -> Objects.equals(audioBroadcastCatchItem.getDeviceId(), deviceId)); + List audioBroadcastCatchList = allAudioBroadcastCatchStreamForDevice.collect(Collectors.toList()); + if (audioBroadcastCatchList.size() == 1 && Objects.equals(config.getId(), channelId)) { + audioBroadcastCatch = audioBroadcastCatchList.get(0); + } + } + + return audioBroadcastCatch; + } + + public List get(String deviceId) { + List audioBroadcastCatchList= new ArrayList<>(); + if (SipUtils.isFrontEnd(deviceId)) { + if (data.get(deviceId) != null) { + audioBroadcastCatchList.add(data.get(deviceId)); + } + }else { + for (String key : data.keySet()) { + if (key.startsWith(deviceId)) { + audioBroadcastCatchList.add(data.get(key)); + } + } + } + + return audioBroadcastCatchList; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/CatalogDataCatch.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/CatalogDataCatch.java new file mode 100644 index 0000000..a40e0a3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/CatalogDataCatch.java @@ -0,0 +1,146 @@ +package com.yfd.monitor.gdw2019.session; + +import com.yfd.monitor.gdw2019.bean.CatalogData; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.SyncStatus; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@Component +public class CatalogDataCatch { + + public static Map data = new ConcurrentHashMap<>(); + + @Autowired + private IVideoManagerStorage storager; + + public void addReady(Device device, int sn ) { + CatalogData catalogData = data.get(device.getDeviceId()); + if (catalogData == null || catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { + catalogData = new CatalogData(); + catalogData.setChannelList(Collections.synchronizedList(new ArrayList<>())); + catalogData.setDevice(device); + catalogData.setSn(sn); + catalogData.setStatus(CatalogData.CatalogDataStatus.ready); + catalogData.setLastTime(Instant.now()); + data.put(device.getDeviceId(), catalogData); + } + } + + public void put(String deviceId, int sn, int total, Device device, List deviceChannelList) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + catalogData = new CatalogData(); + catalogData.setSn(sn); + catalogData.setTotal(total); + catalogData.setDevice(device); + catalogData.setChannelList(deviceChannelList); + catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); + catalogData.setLastTime(Instant.now()); + data.put(deviceId, catalogData); + }else { + // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 + if (catalogData.getSn() != sn) { + return; + } + catalogData.setTotal(total); + catalogData.setDevice(device); + catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); + catalogData.getChannelList().addAll(deviceChannelList); + catalogData.setLastTime(Instant.now()); + } + } + + public List get(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return null; + } + return catalogData.getChannelList(); + } + + public int getTotal(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return 0; + } + return catalogData.getTotal(); + } + + public SyncStatus getSyncStatus(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return null; + } + SyncStatus syncStatus = new SyncStatus(); + syncStatus.setCurrent(catalogData.getChannelList().size()); + syncStatus.setTotal(catalogData.getTotal()); + syncStatus.setErrorMsg(catalogData.getErrorMsg()); + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { + syncStatus.setSyncIng(false); + }else { + syncStatus.setSyncIng(true); + } + return syncStatus; + } + + public boolean isSyncRunning(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return false; + } + return !catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end); + } + +// @Scheduled(fixedRate = 60 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 + private void timerTask(){ + Set keys = data.keySet(); + + Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); + Instant instantBefore30S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(30)); + + for (String deviceId : keys) { + CatalogData catalogData = data.get(deviceId); + if ( catalogData.getLastTime().isBefore(instantBefore5S)) { + // 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作 + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { + if (catalogData.getTotal() == catalogData.getChannelList().size()) { + storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); + }else { + storager.updateChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); + } + String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条"; + catalogData.setErrorMsg(errorMsg); + if (catalogData.getTotal() != catalogData.getChannelList().size()) { + + } + }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) { + String errorMsg = "同步失败,等待回复超时"; + catalogData.setErrorMsg(errorMsg); + } + catalogData.setStatus(CatalogData.CatalogDataStatus.end); + } + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getLastTime().isBefore(instantBefore30S)) { // 超过三十秒,如果标记为end则删除 + data.remove(deviceId); + } + } + } + + + public void setChannelSyncEnd(String deviceId, String errorMsg) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return; + } + catalogData.setStatus(CatalogData.CatalogDataStatus.end); + catalogData.setErrorMsg(errorMsg); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/RecordDataCatch.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/RecordDataCatch.java new file mode 100644 index 0000000..eea9555 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/RecordDataCatch.java @@ -0,0 +1,90 @@ +package com.yfd.monitor.gdw2019.session; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.record.RecordEndEventListener; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + + +@Component +public class RecordDataCatch { + + public static Map data = new ConcurrentHashMap<>(); + + @Autowired + private DeferredResultHolder deferredResultHolder; + @Autowired + private RecordEndEventListener recordEndEventListener; + + + public int put(String deviceId,String channelId, String sn, int sumNum, List recordItems) { + String key = deviceId + sn; + RecordInfo recordInfo = data.get(key); + if (recordInfo == null) { + recordInfo = new RecordInfo(); + recordInfo.setDeviceId(deviceId); + recordInfo.setChannelId(channelId); + recordInfo.setSn(sn.trim()); + recordInfo.setSumNum(sumNum); + recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>())); + recordInfo.setLastTime(Instant.now()); + recordInfo.getRecordList().addAll(recordItems); + data.put(key, recordInfo); + }else { + // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 + if (!Objects.equals(sn.trim(), recordInfo.getSn())) { + return 0; + } + recordInfo.getRecordList().addAll(recordItems); + recordInfo.setLastTime(Instant.now()); + } + return recordInfo.getRecordList().size(); + } + + @Scheduled(fixedRate = 60 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 + private void timerTask(){ + Set keys = data.keySet(); + // 获取五秒前的时刻 + Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(60)); + for (String key : keys) { + RecordInfo recordInfo = data.get(key); + // 超过五秒收不到消息任务超时, 只更新这一部分数据 + if ( recordInfo.getLastTime().isBefore(instantBefore5S)) { + // 处理录像数据, 返回给前端 + String msgKey = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn(); + + // 对数据进行排序 + Collections.sort(recordInfo.getRecordList()); + + RequestMessage msg = new RequestMessage(); + msg.setKey(msgKey); + msg.setData(recordInfo); + deferredResultHolder.invokeAllResult(msg); + recordEndEventListener.delEndEventHandler(recordInfo.getDeviceId(),recordInfo.getChannelId()); + data.remove(key); + } + } + } + + public boolean isComplete(String deviceId, String sn) { + RecordInfo recordInfo = data.get(deviceId + sn); + return recordInfo != null && recordInfo.getRecordList().size() == recordInfo.getSumNum(); + } + + public RecordInfo getRecordInfo(String deviceId, String sn) { + return data.get(deviceId + sn); + } + + public void remove(String deviceId, String sn) { + data.remove(deviceId + sn); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/SSRCFactory.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/SSRCFactory.java new file mode 100644 index 0000000..eb39da4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/SSRCFactory.java @@ -0,0 +1,132 @@ +package com.yfd.monitor.gdw2019.session; + +import com.yfd.monitor.conf.SipConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * ssrc使用 + */ +@Component +public class SSRCFactory { + + /** + * 播流最大并发个数 + */ + private static final Integer MAX_STREAM_COUNT = 10000; + + /** + * 播流最大并发个数 + */ + private static final String SSRC_INFO_KEY = "VMP_SSRC_INFO_"; + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private SipConfig sipConfig; + + + public void initMediaServerSSRC(String mediaServerId, Set usedSet) { + String ssrcPrefix = sipConfig.getDomain().substring(3, 8); + String redisKey = SSRC_INFO_KEY + mediaServerId; + List ssrcList = new ArrayList<>(); + for (int i = 1; i < MAX_STREAM_COUNT; i++) { + String ssrc = String.format("%s%04d", ssrcPrefix, i); + + if (null == usedSet || !usedSet.contains(ssrc)) { + ssrcList.add(ssrc); + + } + } + if (redisTemplate.opsForSet().size(redisKey) != null) { + redisTemplate.delete(redisKey); + } + redisTemplate.opsForSet().add(redisKey, ssrcList.toArray(new String[0])); + } + + + /** + * 获取视频预览的SSRC值,第一位固定为0 + * + * @return ssrc + */ + public String getPlaySsrc(String mediaServerId) { + return "0" + getSN(mediaServerId); + } + + /** + * 获取录像回放的SSRC值,第一位固定为1 + */ + public String getPlayBackSsrc(String mediaServerId) { + return "1" + getSN(mediaServerId); + } + + /** + * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽 + * + * @param ssrc 需要重置的ssrc + */ + public void releaseSsrc(String mediaServerId, String ssrc) { + if (ssrc == null) { + return; + } + String sn = ssrc.substring(1); + String redisKey = SSRC_INFO_KEY + mediaServerId; + redisTemplate.opsForSet().add(redisKey, sn); + } + + /** + * 获取后四位数SN,随机数 + */ + private String getSN(String mediaServerId) { + String sn = null; + String redisKey = SSRC_INFO_KEY + mediaServerId; + Long size = redisTemplate.opsForSet().size(redisKey); + if (size == null || size == 0) { + throw new RuntimeException("ssrc已经用完"); + } else { + // 在集合中移除并返回一个随机成员。 + sn = redisTemplate.opsForSet().pop(redisKey); + redisTemplate.opsForSet().remove(redisKey, sn); + } + return sn; + } + + /** + * 重置一个流媒体服务的所有ssrc + * + * @param mediaServerId 流媒体服务ID + */ + public void reset(String mediaServerId) { + this.initMediaServerSSRC(mediaServerId, null); + } + + /** + * 是否已经存在了某个MediaServer的SSRC信息 + * + * @param mediaServerId 流媒体服务ID + */ + public boolean hasMediaServerSSRC(String mediaServerId) { + String redisKey = SSRC_INFO_KEY + mediaServerId; + return redisTemplate.opsForSet().members(redisKey) != null; + } + + /** + * 查询ssrc是否可用 + * + * @param mediaServerId + * @param ssrc + * @return + */ + public boolean checkSsrc(String mediaServerId, String ssrc) { + String sn = ssrc.substring(1); + String redisKey = SSRC_INFO_KEY + mediaServerId; + return redisTemplate.opsForSet().isMember(redisKey, sn) != null; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/VideoStreamSessionManager.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/VideoStreamSessionManager.java new file mode 100644 index 0000000..9a0e613 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/session/VideoStreamSessionManager.java @@ -0,0 +1,149 @@ +package com.yfd.monitor.gdw2019.session; + +import com.yfd.monitor.common.InviteSessionType; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.bean.SsrcTransaction; +import com.yfd.monitor.utils.JsonUtil; +import com.yfd.monitor.utils.redis.RedisUtil; +import gov.nist.javax.sip.message.SIPResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 视频流session管理器,管理视频预览、预览回放的通信句柄 + */ +@Component +public class VideoStreamSessionManager { + + @Autowired + private UserSetting userSetting; + + @Autowired + private RedisTemplate redisTemplate; + + public enum SessionType { + play, + playback, + download, + BROADCAST, + TALK + } + + /** + * 添加一个点播/回放的事务信息 + * 后续可以通过流Id/callID + * @param deviceId 设备ID + * @param channelId 通道ID + * @param callId 一次请求的CallID + * @param stream 流名称 + * @param mediaServerId 所使用的流媒体ID + * @param response 回复 + * @param type + */ + public void put(String deviceId, String channelId, String callId, String stream, String ssrc, String mediaServerId, SIPResponse response, SessionType type){ + SsrcTransaction ssrcTransaction = new SsrcTransaction(); + ssrcTransaction.setDeviceId(deviceId); + ssrcTransaction.setChannelId(channelId); + ssrcTransaction.setStream(stream); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); + ssrcTransaction.setCallId(callId); + ssrcTransaction.setSsrc(ssrc); + ssrcTransaction.setMediaServerId(mediaServerId); + ssrcTransaction.setType(type); + + redisTemplate.opsForValue().set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + + "_" + deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction); + } + + + public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){ + + if (ObjectUtils.isEmpty(deviceId)) { + deviceId ="*"; + } + if (ObjectUtils.isEmpty(channelId)) { + channelId ="*"; + } + if (ObjectUtils.isEmpty(callId)) { + callId ="*"; + } + if (ObjectUtils.isEmpty(stream)) { + stream ="*"; + } + String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() == 0) { + return null; + } + return (SsrcTransaction)redisTemplate.opsForValue().get(scanResult.get(0)); + } + + public List getSsrcTransactionForAll(String deviceId, String channelId, String callId, String stream){ + if (ObjectUtils.isEmpty(deviceId)) { + deviceId ="*"; + } + if (ObjectUtils.isEmpty(channelId)) { + channelId ="*"; + } + if (ObjectUtils.isEmpty(callId)) { + callId ="*"; + } + if (ObjectUtils.isEmpty(stream)) { + stream ="*"; + } + String key = VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + deviceId + "_" + channelId + "_" + callId+ "_" + stream; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() == 0) { + return null; + } + List result = new ArrayList<>(); + for (Object keyObj : scanResult) { + result.add((SsrcTransaction)redisTemplate.opsForValue().get(keyObj)); + } + return result; + } + + public String getMediaServerId(String deviceId, String channelId, String stream){ + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return null; + } + return ssrcTransaction.getMediaServerId(); + } + + public String getSSRC(String deviceId, String channelId, String stream){ + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return null; + } + return ssrcTransaction.getSsrc(); + } + + public void remove(String deviceId, String channelId, String stream) { + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return; + } + redisTemplate.delete(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + + deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream()); + } + + + public List getAllSsrc() { + List ssrcTransactionKeys = RedisUtil.scan(redisTemplate, String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId())); + List result= new ArrayList<>(); + for (Object ssrcTransactionKey : ssrcTransactionKeys) { + String key = (String) ssrcTransactionKey; + SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(redisTemplate, key, SsrcTransaction.class); + result.add(ssrcTransaction); + } + return result; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/ISubscribeTask.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/ISubscribeTask.java new file mode 100644 index 0000000..2f630bd --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/ISubscribeTask.java @@ -0,0 +1,8 @@ +package com.yfd.monitor.gdw2019.task; + +import javax.sip.DialogState; + + +public interface ISubscribeTask extends Runnable{ + void stop(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/SipRunner.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/SipRunner.java new file mode 100644 index 0000000..c2a42b1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/SipRunner.java @@ -0,0 +1,115 @@ +package com.yfd.monitor.gdw2019.task; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IPlatformService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 系统启动时控制设备 +* + */ +@Component +@Order(value=14) +public class SipRunner implements CommandLineRunner { + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IPlatformService platformService; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Override + public void run(String... args) throws Exception { + List deviceList = deviceService.getAllOnlineDevice(); + + for (Device device : deviceList) { + if (deviceService.expire(device)){ + deviceService.offline(device.getDeviceId(), "注册已过期"); + }else { + deviceService.online(device, null); + } + } + // 重置cseq计数 + redisCatchStorage.resetAllCSEQ(); + // 清理redis + // 清理数据库不存在但是redis中存在的数据 + List devicesInDb = deviceService.getAll(); + if (devicesInDb.size() == 0) { + redisCatchStorage.removeAllDevice(); + }else { + List devicesInRedis = redisCatchStorage.getAllDevices(); + if (devicesInRedis.size() > 0) { + Map deviceMapInDb = new HashMap<>(); + devicesInDb.parallelStream().forEach(device -> { + deviceMapInDb.put(device.getDeviceId(), device); + }); + devicesInRedis.parallelStream().forEach(device -> { + if (deviceMapInDb.get(device.getDeviceId()) == null) { + redisCatchStorage.removeDevice(device.getDeviceId()); + } + }); + } + } + + + // 查找国标推流 + List sendRtpItems = redisCatchStorage.queryAllSendRTPServer(); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStreamId()); + if (mediaServerItem != null) { + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",sendRtpItem.getApp()); + param.put("stream",sendRtpItem.getStreamId()); + param.put("ssrc",sendRtpItem.getSsrc()); + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); + if (platform != null) { + commanderForPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); + } + } + } + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/CatalogSubscribeTask.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/CatalogSubscribeTask.java new file mode 100644 index 0000000..323fbba --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/CatalogSubscribeTask.java @@ -0,0 +1,105 @@ +package com.yfd.monitor.gdw2019.task.impl; + +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.task.ISubscribeTask; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import javax.sip.*; +import javax.sip.header.ToHeader; +import java.text.ParseException; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 目录订阅任务 +* + */ +public class CatalogSubscribeTask implements ISubscribeTask { + private final Logger logger = LoggerFactory.getLogger(CatalogSubscribeTask.class); + private Device device; + private final ISIPCommander sipCommander; + private SIPRequest request; + + private DynamicTask dynamicTask; + + private String taskKey = "catalog-subscribe-timeout"; + + + public CatalogSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) { + this.device = device; + this.sipCommander = sipCommander; + this.dynamicTask = dynamicTask; + } + + @Override + public void run() { + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + SIPRequest sipRequest = null; + try { + sipRequest = sipCommander.catalogSubscribe(device, request, eventResult -> { + ResponseEvent event = (ResponseEvent) eventResult.event; + // 成功 + logger.info("[目录订阅]成功: {}", device.getDeviceId()); + ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME); + try { + this.request.getToHeader().setTag(toHeader.getTag()); + } catch (ParseException e) { + logger.info("[目录订阅]成功: 但为request设置ToTag失败"); + this.request = null; + } + },eventResult -> { + this.request = null; + // 失败 + logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + dynamicTask.startDelay(taskKey, CatalogSubscribeTask.this, 2000); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 目录订阅: {}", e.getMessage()); + + } + if (sipRequest != null) { + this.request = sipRequest; + } + } + + @Override + public void stop() { + /** + * dialog 的各个状态 + * EARLY-> Early state状态-初始请求发送以后,收到了一个临时响应消息 + * CONFIRMED-> Confirmed Dialog状态-已确认 + * COMPLETED-> Completed Dialog状态-已完成 + * TERMINATED-> Terminated Dialog状态-终止 + */ + logger.info("取消目录订阅时dialog状态为{}", DialogState.CONFIRMED); + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + device.setSubscribeCycleForCatalog(0); + try { + sipCommander.catalogSubscribe(device, request, eventResult -> { + ResponseEvent event = (ResponseEvent) eventResult.event; + if (event.getResponse().getRawContent() != null) { + // 成功 + logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId()); + }else { + // 成功 + logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId()); + } + },eventResult -> { + // 失败 + logger.warn("[取消目录订阅订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 取消目录订阅订阅: {}", e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/MobilePositionSubscribeHandlerTask.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/MobilePositionSubscribeHandlerTask.java new file mode 100644 index 0000000..9cc56d1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/MobilePositionSubscribeHandlerTask.java @@ -0,0 +1,44 @@ +package com.yfd.monitor.gdw2019.task.impl; + +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.task.ISubscribeTask; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.service.IPlatformService; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.SpringBeanFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; + +import javax.sip.DialogState; +import java.util.List; + +/** + * 向已经订阅(移动位置)的上级发送MobilePosition消息 +* + */ +public class MobilePositionSubscribeHandlerTask implements ISubscribeTask { + + + private IPlatformService platformService; + private String platformId; + + + public MobilePositionSubscribeHandlerTask(String platformId) { + this.platformService = SpringBeanFactory.getBean("platformServiceImpl"); + this.platformId = platformId; + } + + @Override + public void run() { + platformService.sendNotifyMobilePosition(this.platformId); + } + + @Override + public void stop() { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/MobilePositionSubscribeTask.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/MobilePositionSubscribeTask.java new file mode 100644 index 0000000..5fdaa98 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/task/impl/MobilePositionSubscribeTask.java @@ -0,0 +1,103 @@ +package com.yfd.monitor.gdw2019.task.impl; + +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.task.ISubscribeTask; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; + +import javax.sip.*; +import javax.sip.header.ToHeader; +import java.text.ParseException; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 移动位置订阅的定时更新 +* + */ +public class MobilePositionSubscribeTask implements ISubscribeTask { + private final Logger logger = LoggerFactory.getLogger(MobilePositionSubscribeTask.class); + private Device device; + private ISIPCommander sipCommander; + + private SIPRequest request; + private DynamicTask dynamicTask; + private String taskKey = "mobile-position-subscribe-timeout"; + + public MobilePositionSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) { + this.device = device; + this.sipCommander = sipCommander; + this.dynamicTask = dynamicTask; + } + + @Override + public void run() { + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + SIPRequest sipRequest = null; + try { + sipRequest = sipCommander.mobilePositionSubscribe(device, request, eventResult -> { + // 成功 + logger.info("[移动位置订阅]成功: {}", device.getDeviceId()); + ResponseEvent event = (ResponseEvent) eventResult.event; + ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME); + try { + this.request.getToHeader().setTag(toHeader.getTag()); + } catch (ParseException e) { + logger.info("[移动位置订阅]成功: 为request设置ToTag失败"); + this.request = null; + } + },eventResult -> { + this.request = null; + // 失败 + logger.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + dynamicTask.startDelay(taskKey, MobilePositionSubscribeTask.this, 2000); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 移动位置订阅: {}", e.getMessage()); + } + if (sipRequest != null) { + this.request = sipRequest; + } + + } + + @Override + public void stop() { + /** + * dialog 的各个状态 + * EARLY-> Early state状态-初始请求发送以后,收到了一个临时响应消息 + * CONFIRMED-> Confirmed Dialog状态-已确认 + * COMPLETED-> Completed Dialog状态-已完成 + * TERMINATED-> Terminated Dialog状态-终止 + */ + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + device.setSubscribeCycleForMobilePosition(0); + try { + sipCommander.mobilePositionSubscribe(device, request, eventResult -> { + ResponseEvent event = (ResponseEvent) eventResult.event; + if (event.getResponse().getRawContent() != null) { + // 成功 + logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); + }else { + // 成功 + logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); + } + },eventResult -> { + // 失败 + logger.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 取消移动位置订阅: {}", e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/ISIPProcessorObserver.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/ISIPProcessorObserver.java new file mode 100644 index 0000000..466f738 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/ISIPProcessorObserver.java @@ -0,0 +1,6 @@ +package com.yfd.monitor.gdw2019.transmit; + +import javax.sip.SipListener; + +public interface ISIPProcessorObserver extends SipListener { +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/SIPProcessorObserver.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/SIPProcessorObserver.java new file mode 100644 index 0000000..d24a348 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/SIPProcessorObserver.java @@ -0,0 +1,205 @@ +package com.yfd.monitor.gdw2019.transmit; + +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.response.ISIPResponseProcessor; +import com.yfd.monitor.gdw2019.transmit.event.timeout.ITimeoutProcessor; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.sip.*; +import javax.sip.header.*; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.net.InetAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @description: SIP信令处理类观察者 + * @date: 2021年11月5日 下午15:32 + */ +@Component +public class SIPProcessorObserver implements ISIPProcessorObserver { + + private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class); + + private static Map requestProcessorMap = new ConcurrentHashMap<>(); + private static Map responseProcessorMap = new ConcurrentHashMap<>(); + private static ITimeoutProcessor timeoutProcessor; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private EventPublisher eventPublisher; + + /** + * 添加 request订阅 + * @param method 方法名 + * @param processor 处理程序 + */ + public void addRequestProcessor(String method, ISIPRequestProcessor processor) { + requestProcessorMap.put(method, processor); + } + + /** + * 添加 response订阅 + * @param method 方法名 + * @param processor 处理程序 + */ + public void addResponseProcessor(String method, ISIPResponseProcessor processor) { + responseProcessorMap.put(method, processor); + } + + /** + * 添加 超时事件订阅 + * @param processor 处理程序 + */ + public void addTimeoutProcessor(ITimeoutProcessor processor) { + timeoutProcessor = processor; + } + + /** + * 分发RequestEvent事件 + * @param requestEvent RequestEvent事件 + */ + @Override + @Async("taskExecutor") + public void processRequest(RequestEvent requestEvent) { + String method = requestEvent.getRequest().getMethod(); + ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method); + if (sipRequestProcessor == null) { + logger.warn("不支持方法{}的request", method); + // TODO 回复错误玛 + return; + } + requestProcessorMap.get(method).process(requestEvent); + + } + + /** + * 分发ResponseEvent事件 + * @param responseEvent responseEvent事件 + */ + @Override + @Async("taskExecutor") + public void processResponse(ResponseEvent responseEvent) { + Response response = responseEvent.getResponse(); + int status = response.getStatusCode(); + + // Success + if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) { + CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME); + String method = cseqHeader.getMethod(); + ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method); + if (sipRequestProcessor != null) { + sipRequestProcessor.process(responseEvent); + } + if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { + CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); + if (callIdHeader != null) { + SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId()); + if (subscribe != null) { + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); + sipSubscribe.removeOkSubscribe(callIdHeader.getCallId()); + subscribe.response(eventResult); + } + } + } + } else if ((status >= Response.TRYING) && (status < Response.OK)) { + // 增加其它无需回复的响应,如101、180等 + } else { + logger.error("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()); + logger.error("接收到失败的response响应!status:"+response.toString()); + if (responseEvent.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { + CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); + if (callIdHeader != null) { + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); + if (subscribe != null) { + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); + subscribe.response(eventResult); + sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); + } + } + } + if (responseEvent.getDialog() != null) { + responseEvent.getDialog().delete(); + } + } + + + } + + /** + * 向超时订阅发送消息 + * @param timeoutEvent timeoutEvent事件 + */ + @Override + public void processTimeout(TimeoutEvent timeoutEvent) { + logger.info("[消息发送超时]"); + ClientTransaction clientTransaction = timeoutEvent.getClientTransaction(); + + if (clientTransaction != null) { + logger.info("[发送错误订阅] clientTransaction != null"); + Request request = clientTransaction.getRequest(); + if (request != null) { + logger.info("[发送错误订阅] request != null"); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + if (callIdHeader != null) { + logger.info("[发送错误订阅]"); + SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(timeoutEvent); + if (subscribe != null){ + subscribe.response(eventResult); + } + sipSubscribe.removeOkSubscribe(callIdHeader.getCallId()); + sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); + } + } + } + eventPublisher.requestTimeOut(timeoutEvent); + } + + @Override + public void processIOException(IOExceptionEvent exceptionEvent) { + logger.info("processIOException"); + } + + @Override + public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { +// if (transactionTerminatedEvent.isServerTransaction()) { +// ServerTransaction serverTransaction = transactionTerminatedEvent.getServerTransaction(); +// serverTransaction.get +// } + + +// Transaction transaction = null; +// System.out.println("processTransactionTerminated"); +// if (transactionTerminatedEvent.isServerTransaction()) { +// transaction = transactionTerminatedEvent.getServerTransaction(); +// }else { +// transaction = transactionTerminatedEvent.getClientTransaction(); +// } +// +// System.out.println(transaction.getBranchId()); +// System.out.println(transaction.getState()); +// System.out.println(transaction.getRequest().getMethod()); +// CallIdHeader header = (CallIdHeader)transaction.getRequest().getHeader(CallIdHeader.NAME); +// SipSubscribe.EventResult terminatedEventEventResult = new SipSubscribe.EventResult<>(transactionTerminatedEvent); + +// sipSubscribe.getErrorSubscribe(header.getCallId()).response(terminatedEventEventResult); + } + + @Override + public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) { + CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId(); + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/SIPSender.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/SIPSender.java new file mode 100644 index 0000000..9e6d88a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/SIPSender.java @@ -0,0 +1,137 @@ +package com.yfd.monitor.gdw2019.transmit; + +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.utils.GitUtil; +import gov.nist.javax.sip.SipProviderImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.header.UserAgentHeader; +import javax.sip.header.ViaHeader; +import javax.sip.message.Message; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.text.ParseException; + +/** + * 发送SIP消息 + */ +@Component +public class SIPSender { + + private final Logger logger = LoggerFactory.getLogger(SIPSender.class); + + @Autowired + private SipLayer sipLayer; + + @Autowired + private GitUtil gitUtil; + + @Autowired + private SipSubscribe sipSubscribe; + + public void transmitRequest(String ip, Message message) throws SipException, ParseException { + transmitRequest(ip, message, null, null); + } + + public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent) throws SipException, ParseException { + transmitRequest(ip, message, errorEvent, null); + } + + public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException { + ViaHeader viaHeader = (ViaHeader) message.getHeader(ViaHeader.NAME); + String transport = "UDP"; + if (viaHeader == null) { + logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据"); + } else { + transport = viaHeader.getTransport(); + } + if (message.getHeader(UserAgentHeader.NAME) == null) { + try { + message.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + } catch (ParseException e) { + logger.error("添加UserAgentHeader失败", e); + } + } + + CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); + // 添加错误订阅 + if (errorEvent != null) { + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> { + errorEvent.response(eventResult); + sipSubscribe.removeErrorSubscribe(eventResult.callId); + sipSubscribe.removeOkSubscribe(eventResult.callId); + })); + } + // 添加订阅 + if (okEvent != null) { + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> { + okEvent.response(eventResult); + sipSubscribe.removeOkSubscribe(eventResult.callId); + sipSubscribe.removeErrorSubscribe(eventResult.callId); + }); + } + if ("TCP".equals(transport)) { + SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip); + if ("22.58.167.120".equals(ip)||"192.168.1.83".equals(ip)) { + logger.error("B接口发送信息TCP" + message); + } + if (tcpSipProvider == null) { + logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip); + return; + } + if (message instanceof Request) { + tcpSipProvider.sendRequest((Request) message); + } else if (message instanceof Response) { + tcpSipProvider.sendResponse((Response) message); + } + + } else if ("UDP".equals(transport)) { + SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip); + if ("22.58.167.120".equals(ip)||"192.168.1.83".equals(ip)) { + logger.error("B接口发送信息UDP" + message); + } + if (sipProvider == null) { + logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip); + return; + } + if (message instanceof Request) { + sipProvider.sendRequest((Request) message); + } else if (message instanceof Response) { + sipProvider.sendResponse((Response) message); + } + } + } + + public CallIdHeader getNewCallIdHeader(String ip, String transport) { + if (ObjectUtils.isEmpty(transport)) { + return sipLayer.getUdpSipProvider().getNewCallId(); + } + SipProviderImpl sipProvider; + if (ObjectUtils.isEmpty(ip)) { + sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider() + : sipLayer.getUdpSipProvider(); + } else { + sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider(ip) + : sipLayer.getUdpSipProvider(ip); + } + + if (sipProvider == null) { + sipProvider = sipLayer.getUdpSipProvider(); + } + + if (sipProvider != null) { + return sipProvider.getNewCallId(); + } else { + logger.warn("[新建CallIdHeader失败], ip={}, transport={}", ip, transport); + return null; + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/callback/DeferredResultHolder.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/callback/DeferredResultHolder.java new file mode 100644 index 0000000..fa8bb3a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/callback/DeferredResultHolder.java @@ -0,0 +1,152 @@ +package com.yfd.monitor.gdw2019.transmit.callback; + +import com.yfd.monitor.vmanager.bean.DeferredResultEx; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.async.DeferredResult; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @description: 异步请求处理 + * @date: 2020年5月8日 下午7:59:05 + */ +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Component +public class DeferredResultHolder { + + public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS"; + + public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO"; + + public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL"; + + public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG"; + + public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD"; + + public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG"; + + public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO"; + + public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY"; + + public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAYBACK"; + + public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD"; + + public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; + + public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL"; + + public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; + + public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; + + public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM"; + + public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; + + private Map> map = new ConcurrentHashMap<>(); + + + public void put(String key, String id, DeferredResultEx result) { + Map deferredResultMap = map.get(key); + if (deferredResultMap == null) { + deferredResultMap = new ConcurrentHashMap<>(); + map.put(key, deferredResultMap); + } + deferredResultMap.put(id, result); + } + + public void put(String key, String id, DeferredResult result) { + Map deferredResultMap = map.get(key); + if (deferredResultMap == null) { + deferredResultMap = new ConcurrentHashMap<>(); + map.put(key, deferredResultMap); + } + deferredResultMap.put(id, new DeferredResultEx(result)); + } + + public DeferredResultEx get(String key, String id) { + Map deferredResultMap = map.get(key); + if (deferredResultMap == null || ObjectUtils.isEmpty(id)) { + return null; + } + return deferredResultMap.get(id); + } + + public Collection getAllByKey(String key) { + Map deferredResultMap = map.get(key); + if (deferredResultMap == null) { + return null; + } + return deferredResultMap.values(); + } + + public boolean exist(String key, String id){ + if (key == null) { + return false; + } + Map deferredResultMap = map.get(key); + if (id == null) { + return deferredResultMap != null; + }else { + return deferredResultMap != null && deferredResultMap.get(id) != null; + } + } + + /** + * 释放单个请求 + * @param msg + */ + public void invokeResult(RequestMessage msg) { + Map deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { + return; + } + DeferredResultEx result = deferredResultMap.get(msg.getId()); + if (result == null) { + return; + } + result.getDeferredResult().setResult(msg.getData()); + deferredResultMap.remove(msg.getId()); + if (deferredResultMap.size() == 0) { + map.remove(msg.getKey()); + } + } + + /** + * 释放所有的请求 + * @param msg + */ + public void invokeAllResult(RequestMessage msg) { + Map deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { + return; + } + synchronized (this) { + deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { + return; + } + Set ids = deferredResultMap.keySet(); + for (String id : ids) { + DeferredResultEx result = deferredResultMap.get(id); + if (result == null) { + return; + } + if (result.getFilter() != null) { + Object handler = result.getFilter().handler(msg.getData()); + result.getDeferredResult().setResult(handler); + }else { + result.getDeferredResult().setResult(msg.getData()); + } + } + map.remove(msg.getKey()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/callback/RequestMessage.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/callback/RequestMessage.java new file mode 100644 index 0000000..392fe87 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/callback/RequestMessage.java @@ -0,0 +1,38 @@ +package com.yfd.monitor.gdw2019.transmit.callback; + +/** + * @description: 请求信息定义 + * @date: 2020年5月8日 下午1:09:18 + */ +public class RequestMessage { + + private String id; + + private String key; + + private Object data; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public void setKey(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommander.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommander.java new file mode 100644 index 0000000..dd6650a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommander.java @@ -0,0 +1,390 @@ +package com.yfd.monitor.gdw2019.transmit.cmd; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.media.event.hook.HookSubscribe; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.bean.SSRCInfo; +import gov.nist.javax.sip.message.SIPRequest; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * @description:设备能力接口,用于定义设备的控制、查询能力 +* + * @date: 2020年5月3日 下午9:16:34 + */ +public interface ISIPCommander { + + /** + * 云台方向放控制,使用配置文件中的默认镜头移动速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + */ + void ptzdirectCmd(Device device,String channelId,int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException; + + /** + * 云台方向放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param moveSpeed 镜头移动速度 + */ + void ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException; + + /** + * 云台缩放控制,使用配置文件中的默认镜头缩放速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + */ + void ptzZoomCmd(Device device,String channelId,int inOut) throws InvalidArgumentException, ParseException, SipException; + + /** + * 云台缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + */ + void ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed) throws InvalidArgumentException, ParseException, SipException; + + /** + * 云台控制,支持方向与缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 + * @param zoomSpeed 镜头缩放速度 + */ + void ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) throws InvalidArgumentException, SipException, ParseException; + + /** + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException; + + /** + * 前端控制指令(用于转发上级指令) + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdString 前端控制指令串 + */ + void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 请求预览视频流 + * @param device 视频设备 + * @param channelId 预览通道 + */ + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 请求预览视频流 + * @param device 视频设备 + * @param channelId 预览通道 + */ + void playStreamCmd1(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, HookSubscribe.Event event1) throws InvalidArgumentException, SipException, ParseException; + + /** + * 请求预览视频流 + * @param device 视频设备 + * @param ssrcInfo 预览通道 + */ + void inviteVideo(Device device, String channelId,String sdp, MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 请求语音广播 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + void audioBroadcastCmd(String type,Device device, String channelId, String sdp, MediaServerItem mediaServerItem,SSRCInfo ssrcInfo, SipSubscribe.Event okEvent)throws InvalidArgumentException, SipException, ParseException ; + + /** + * 请求回放视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 请求历史媒体下载 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param downloadSpeed 下载倍速参数 + */ + void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 视频流停止 + */ + void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; + + void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException; + + /** + * 回放暂停 + */ + void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放恢复 + */ + void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放拖动播放 + */ + void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放倍速播放 + */ + void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException; + + /** + * 回放控制 + * @param device + * @param streamInfo + * @param content + */ + void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; + + + + + /** + * 语音广播 + * + * @param device 视频设备 + */ + void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException; + + /** + * 音视频录像控制 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param recordCmdStr 录像命令:Record / StopRecord + */ + void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 远程启动控制命令 + * + * @param device 视频设备 + */ + void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException; + + /** + * 报警布防/撤防命令 + * + * @param device 视频设备 + */ + void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 报警复位命令 + * + * @param device 视频设备 + * @param alarmMethod 报警方式(可选) + * @param alarmType 报警类型(可选) + */ + void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException; + + /** + * 看守位控制命令 + * + * @param device 视频设备 + * @param channelId 通道id,非通道则是设备本身 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + */ + void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 设备配置命令 + * + * @param device 视频设备 + */ + void deviceConfigCmd(Device device); + + /** + * 设备配置命令:basicParam + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param name 设备/通道名称(可选) + * @param expiration 注册过期时间(可选) + * @param heartBeatInterval 心跳间隔时间(可选) + * @param heartBeatCount 心跳超时次数(可选) + */ + void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询设备状态 + * + * @param device 视频设备 + */ + void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询设备信息 + * + * @param device 视频设备 + * @return + */ + void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询目录列表 + * + * @param device 视频设备 + */ + void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException; + + /** + * 查询录像信息 + * + * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param sn + */ + void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer Secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询报警信息 + * + * @param device 视频设备 + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, + String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询设备配置 + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param configType 配置类型: + */ + void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询设备预置位置 + * + * @param device 视频设备 + */ + void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 查询移动设备位置数据 + * + * @param device 视频设备 + */ + void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 订阅、取消订阅移动位置 + * + * @param device 视频设备 + * @return true = 命令发送成功 + */ + SIPRequest mobilePositionSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 订阅、取消订阅报警信息 + * @param device 视频设备 + * @param expires 订阅过期时间(0 = 取消订阅) + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException; + + /** + * 订阅、取消订阅目录信息 + * @param device 视频设备 + * @return true = 命令发送成功 + */ + SIPRequest catalogSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 拉框控制命令 + * + * @param device 控制设备 + * @param channelId 通道id + * @param cmdString 前端控制指令串 + */ + void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException; + + + /** + * 向设备发送报警NOTIFY消息, 用于互联结构下,此时将设备当成一个平级平台看待 + * @param device 设备 + * @param deviceAlarm 报警信息信息 + * @return + */ + void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException; + + void streamByeCmdForDeviceInvite(Device device, String channelId, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; + void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, String channelId, String callId, HookSubscribe.Event event, HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + /** + * /** + * 语音广播 + * + * @param device 视频设备 + */ + void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommanderForNode.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommanderForNode.java new file mode 100644 index 0000000..1320cb4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommanderForNode.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.gdw2019.transmit.cmd; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.service.bean.GPSMsgInfo; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.sip.header.WWWAuthenticateHeader; +import java.text.ParseException; +import java.util.List; + +public interface ISIPCommanderForNode { + + + /** + * 向边缘节点下发巡视任务配置 + * @param stationnode + * @return + */ + void sendTaskToNode(SubstationNode stationnode) throws InvalidArgumentException, ParseException, SipException; + + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommanderForPlatform.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommanderForPlatform.java new file mode 100644 index 0000000..f08fedc --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/ISIPCommanderForPlatform.java @@ -0,0 +1,141 @@ +package com.yfd.monitor.gdw2019.transmit.cmd; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import org.springframework.lang.Nullable; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.sip.header.WWWAuthenticateHeader; +import java.text.ParseException; +import java.util.List; +import java.util.Map; + +public interface ISIPCommanderForPlatform { + + /** + * 向上级平台注册 + * @param parentPlatform + * @return + */ + + //开机首次注册 + void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + + + void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + + //携带授权二次注册 + void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException; + + /** + * 向上级平台注销 + * @param parentPlatform + * @return + */ + void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + + //向上级主动同步目录信息 + void sendCatalogNotice(ParentPlatform parentPlatform,@Nullable SipTransactionInfo sipTransactionInfo) throws InvalidArgumentException, ParseException, SipException; + + /** + * 向上级回复通道信息 + * @param Code 通道信息 + * @param FromIndex + * @param ToIndex + * @return + */ + String queryCatalog(ParentPlatform parentPlatform,String Code,String FromIndex,String ToIndex,String fromTag) ; + + /** + * 查询设备历史报警信息 + * @param Code 查询告警地址编码,视频通道 + * @param UserCode 用户地址编码 + * @param Type 告警类型,见订阅行为 + * @param BeginTime 开始时间,格式如 1990-01-01T00:00:00Z + * @param EndTime 结束时间,格式如 1990-01-01T00:00:00Z + * @param Level 告警级别,仅当节点为告警且全网有统一的告 + * 警级别规划时有效,0 表示未定义告警级别 + * @param FromIndex + * @param ToIndex + * @return + */ + String queryHistoryAlarm(ParentPlatform parentPlatform,String Code, String UserCode, String Type, String BeginTime, String EndTime, String Level, String FromIndex, String ToIndex) ; + + /** + * 查询设备录像信息 + * @param Code 查询告警地址编码,视频通道 + * @param UserCode 用户地址编码 + * @param Type 告警类型,见订阅行为 + * @param BeginTime 开始时间,格式如 1990-01-01T00:00:00Z + * @param EndTime 结束时间,格式如 1990-01-01T00:00:00Z + * @param FromIndex + * @param ToIndex + * @return + */ + String queryDeviceRecord(ParentPlatform parentPlatform,String Code, String UserCode, String Type, String BeginTime, String EndTime, String FromIndex, String ToIndex) ; + + /** + * 向上级回复DeviceStatus查询信息 + * @param parentPlatform 平台信息 + * @param sn + * @param fromTag + * @return + */ + void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,boolean status) throws SipException, InvalidArgumentException, ParseException; + + + /** + * 向上级回复报警消息 + * @param parentPlatform 平台信息 + * @param deviceAlarms 报警信息信息 + * @return + */ + void sendAlarmMessage(ParentPlatform parentPlatform, List deviceAlarms) throws SipException, InvalidArgumentException, ParseException; + + + + /** + * 回复recordInfo + * @param deviceChannel 通道信息 + * @param parentPlatform 平台信息 + * @param fromTag fromTag + * @param recordInfo 录像信息 + */ + void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException; + + + + + /** + * 从设备上截图,并发送截图通知 + * @param deviceid 摄像头ID + * @param PicServer 截图会传地址 + * @param platform + * @return + */ + void snapImageAndsendNoticeMsg(String deviceid,String PicServer,ParentPlatform platform,@Nullable SipTransactionInfo sipTransactionInfo) throws SipException, InvalidArgumentException, ParseException; + + + /** + * 向上级发送订阅成功的通知 + * @param parentPlatform 平台信息 + * @return + */ + void sendNotify(ParentPlatform parentPlatform, + SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent ) + throws SipException, ParseException, InvalidArgumentException; + + + + void sendAlarmNotice(ParentPlatform parentPlatform, List> alarmitems, @Nullable SipTransactionInfo sipTransactionInfo) throws InvalidArgumentException, ParseException, SipException; + + void sendDeviceStatusNotice(ParentPlatform parentPlatform, List> devicestatus, @Nullable SipTransactionInfo sipTransactionInfo) throws InvalidArgumentException, ParseException, SipException; + /** + * 向发起点播的上级回复bye + * @param platform 平台信息 + * @param callId callId + */ + void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException; + void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/SIPRequestHeaderPlarformProvider.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/SIPRequestHeaderPlarformProvider.java new file mode 100644 index 0000000..58c5ec7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/SIPRequestHeaderPlarformProvider.java @@ -0,0 +1,359 @@ +package com.yfd.monitor.gdw2019.transmit.cmd; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.bean.SubscribeInfo; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.utils.GitUtil; +import gov.nist.javax.sip.message.MessageFactoryImpl; +import gov.nist.javax.sip.message.SIPRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.DigestUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.PeerUnavailableException; +import javax.sip.SipFactory; +import javax.sip.address.Address; +import javax.sip.address.SipURI; +import javax.sip.address.URI; +import javax.sip.header.*; +import javax.sip.message.Request; +import javax.validation.constraints.NotNull; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.UUID; + +/** + * @description: 平台命令request创造器 TODO 冗余代码太多待优化 + * @date: 2020年5月6日 上午9:29:02 + */ +@Component +public class SIPRequestHeaderPlarformProvider { + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private GitUtil gitUtil; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, int expires) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), + parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + //via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), + parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader, + cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() + .createSipURI(parentPlatform.getDeviceGBId(), sipAddress)); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires); + request.addHeader(expiresHeader); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag, + WWWAuthenticateHeader www, CallIdHeader callIdHeader, int expires) throws ParseException, PeerUnavailableException, InvalidArgumentException { + + + Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, expires); + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); +// URI requestURI = SipFactory.getInstance().createAddressFactory().createURI("sip:192.168.1.20"); + if (www == null) { + AuthorizationHeader authorizationHeader = + SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader("Digest"); + String username = parentPlatform.getUsername(); + if (username == null || "" == username) { + authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); + } else { + authorizationHeader.setUsername(username); + } + authorizationHeader.setURI(requestURI); + authorizationHeader.setAlgorithm("MD5"); + registerRequest.addHeader(authorizationHeader); + return registerRequest; + } + String realm = www.getRealm(); + String nonce = www.getNonce(); + String scheme = www.getScheme(); + + // 参考 https://blog.csdn.net/y673533511/article/details/88388138 + // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 + String qop = www.getQop(); + + String cNonce = null; + String nc = "00000001"; + if (qop != null) { + if ("auth".equalsIgnoreCase(qop)) { + // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 + // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 + cNonce = UUID.randomUUID().toString(); + + } else if ("auth-int".equalsIgnoreCase(qop)) { + // TODO + } + } + String HA1 = DigestUtils.md5DigestAsHex((parentPlatform.getDeviceGBId() + ":" + realm + ":" + parentPlatform.getPassword()).getBytes()); + String HA2 = DigestUtils.md5DigestAsHex((Request.REGISTER + ":" + requestURI.toString()).getBytes()); + + StringBuffer reStr = new StringBuffer(200); + reStr.append(HA1); + reStr.append(":"); + reStr.append(nonce); + reStr.append(":"); + if (qop != null) { + reStr.append(nc); + reStr.append(":"); + reStr.append(cNonce); + reStr.append(":"); + reStr.append(qop); + reStr.append(":"); + } + reStr.append(HA2); + + String RESPONSE = DigestUtils.md5DigestAsHex(reStr.toString().getBytes()); + + AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader(scheme); + authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); + authorizationHeader.setRealm(realm); + authorizationHeader.setNonce(nonce); + authorizationHeader.setURI(requestURI); + authorizationHeader.setResponse(RESPONSE); + authorizationHeader.setAlgorithm("MD5"); + if (qop != null) { + authorizationHeader.setQop(qop); + authorizationHeader.setCNonce(cNonce); + authorizationHeader.setNonceCount(1); + } + String Logout_Reason = www.getParameter("Logout-Reason"); + if (Logout_Reason != null) { + authorizationHeader.setParameter("Logout-Reason", Logout_Reason); + } + registerRequest.addHeader(authorizationHeader); + + return registerRequest; + } + + public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); + return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader); + } + + public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, String viaTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + return createMessageRequest(parentPlatform, content, fromTag, viaTag, null, callIdHeader); + } + + + public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, String viaTag, String toTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + Request request = null; + String serverAddress = parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort(); + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), viaTag); + //viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + // SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp()); + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset(parentPlatform.getCharacterSet()); + request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "xml"); + request.setContent(content, contentTypeHeader); + return request; + } + + //主动上报前端系统资源 + public Request createNoticeRequest2(ParentPlatform parentPlatform, String content, String fromTag, String viaTag, String toTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + Request request = null; + String serverAddress = parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort(); + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + // SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp()); + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset(parentPlatform.getCharacterSet()); + request = messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("application", "xml"); + request.setContent(content, contentTypeHeader); + return request; + } + + public SIPRequest createNotifyRequest(ParentPlatform parentPlatform, SubscribeInfo subscribeInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException { + SIPRequest request = null; + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + // via + ArrayList viaHeaders = new ArrayList<>(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), + parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag()); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag()); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset("gb2312"); + + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId()); + + request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + EventHeader event = SipFactory.getInstance().createHeaderFactory().createEventHeader(subscribeInfo.getEventType()); + if (subscribeInfo.getEventId() != null) { + event.setEventId(subscribeInfo.getEventId()); + } + + request.addHeader(event); + + SubscriptionStateHeader active = SipFactory.getInstance().createHeaderFactory().createSubscriptionStateHeader("active"); + request.setHeader(active); + + String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() + .createSipURI(parentPlatform.getDeviceGBId(), sipAddress)); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + return request; + } + + public SIPRequest createByeRequest(ParentPlatform platform, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + + if (sendRtpItem == null) { + return null; + } + + SIPRequest request = null; + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP() + ":" + platform.getServerPort()); + // via + ArrayList viaHeaders = new ArrayList<>(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()), + platform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getDeviceGBId(), + platform.getDeviceIp() + ":" + platform.getDevicePort()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag()); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset("gb2312"); + + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); + + request = (SIPRequest) messageFactory.createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory() + .createSipURI(platform.getDeviceGBId(), sipAddress)); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + return request; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/SIPRequestHeaderProvider.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/SIPRequestHeaderProvider.java new file mode 100644 index 0000000..9dbc04b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/SIPRequestHeaderProvider.java @@ -0,0 +1,386 @@ +package com.yfd.monitor.gdw2019.transmit.cmd; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.bean.SubstationNode; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.utils.GitUtil; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.PeerUnavailableException; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.address.Address; +import javax.sip.address.SipURI; +import javax.sip.header.*; +import javax.sip.message.Request; +import java.text.ParseException; +import java.util.ArrayList; + +/** + * @description:摄像头命令request创造器 TODO 冗余代码太多待优化 + * @date: 2020年5月6日 上午9:29:02 + */ +@Component +public class SIPRequestHeaderProvider { + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private GitUtil gitUtil; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private VideoStreamSessionManager streamSession; + + public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); + + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); + request.setContent(content, contentTypeHeader); + return request; + } + //向变电站边缘节点的请求封装 + public Request createSationNodeRequest(SubstationNode stationnode, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(stationnode.getStationGBId(), stationnode.getStationIp() + ":" +stationnode.getStationGBPort()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(stationnode.getServerIp()), stationnode.getServerPort(), stationnode.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(stationnode.getServerId(), stationnode.getServerDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(stationnode.getStationGBId(), stationnode.getStationIp()+ ":" +stationnode.getStationGBPort()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, toTag); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); + + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); + request.setContent(content, contentTypeHeader); + return request; + } + + public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + //via + ArrayList viaHeaders = new ArrayList(); + HeaderFactory headerFactory = SipFactory.getInstance().createHeaderFactory(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + // Subject + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); + request.addHeader(subjectHeader); + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + request.setContent(content, contentTypeHeader); + return request; + } + + public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + // Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + // Subject + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); + request.addHeader(subjectHeader); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + request.setContent(content, contentTypeHeader); + return request; + } + + public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + // sipuri + SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), + device.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag()); + // to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag()); + + // Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + // ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE); + + request = SipFactory.getInstance().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + // Expires + ExpiresHeader expireHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires); + request.addHeader(expireHeader); + + // Event + EventHeader eventHeader = SipFactory.getInstance().createHeaderFactory().createEventHeader(event); + + int random = (int) Math.floor(Math.random() * 10000); + eventHeader.setEventId(random + ""); + request.addHeader(eventHeader); + + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); + request.setContent(content, contentTypeHeader); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + public SIPRequest createInfoRequest(Device device, String channelId, String content, SipTransactionInfo transactionInfo) + throws SipException, ParseException, InvalidArgumentException { + if (device == null || transactionInfo == null) { + return null; + } + SIPRequest request = null; + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO); + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = (SIPRequest)SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + if (content != null) { + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", + "MANSRTSP"); + request.setContent(content, contentTypeHeader); + } + return request; + } + + public Request createAckRequest(String localIp, SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException { + + + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK); + + Request request = SipFactory.getInstance().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } + + public Request createByteRequestForDeviceInvite(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException { + Request request = null; + //请求行 + SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + //from + SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getToTag()); + //to + SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, transactionInfo.getFromTag()); + + //Forwards + MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(gitUtil)); + + return request; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommander.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommander.java new file mode 100644 index 0000000..c295a22 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommander.java @@ -0,0 +1,1673 @@ +package com.yfd.monitor.gdw2019.transmit.cmd.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.SIPRequestHeaderProvider; +import com.yfd.monitor.gdw2019.utils.NumericUtil; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.media.event.hook.Hook; +import com.yfd.monitor.media.event.hook.HookSubscribe; +import com.yfd.monitor.media.event.hook.HookType; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.HookSubscribeFactory; +import com.yfd.monitor.media.zlm.dto.HookSubscribeForStreamChange; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Request; +import java.text.ParseException; + +/** + * @description:设备能力接口,用于定义设备的控制、查询能力 + * @date: 2020年5月3日 下午9:22:48 + */ +@Component +@DependsOn("sipLayer") +public class SIPCommander implements ISIPCommander { + + private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SIPSender sipSender; + + @Autowired + private SIPRequestHeaderProvider headerProvider; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private HookSubscribe subscribe1; + + @Autowired + private IMediaServerService mediaServerService; + + /** + * 云台方向放控制,使用配置文件中的默认镜头移动速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + */ + @Override + public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0); + } + + /** + * 云台方向放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param moveSpeed 镜头移动速度 + */ + @Override + public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0); + } + + /** + * 云台缩放控制,使用配置文件中的默认镜头缩放速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + */ + @Override + public void ptzZoomCmd(Device device, String channelId, int inOut) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed()); + } + + /** + * 云台缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param zoomSpeed 镜头缩放速度 + */ + @Override + public void ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed); + } + + /** + * 云台指令码计算 + * + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) { + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter1); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter2); + builder.append(strTmp, 0, 2); + strTmp = String.format("%X", combineCode2); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 云台控制,支持方向与缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 + * @param zoomSpeed 镜头缩放速度 + */ + @Override + public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, + int zoomSpeed) throws InvalidArgumentException, SipException, ParseException { + String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); + StringBuilder ptzXml = new StringBuilder(200); + String charset = device.getCharset(); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdStr + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("5\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + } + + /** + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + @Override + public void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException { + + String cmdStr = frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2); + StringBuffer ptzXml = new StringBuffer(200); + String charset = device.getCharset(); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdStr + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("5\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + + } + + /** + * 前端控制指令(用于转发上级指令) + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdString 前端控制指令串 + */ + @Override + public void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer ptzXml = new StringBuffer(200); + String charset = device.getCharset(); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdString + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("5\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + logger.error("云台xml"+ptzXml.toString()); + logger.error("===========================发送ip"+sipLayer.getLocalIp(device.getLocalIp())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request, errorEvent, okEvent); + + } + + /** + * 请求预览视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param okEvent hook订阅 + * @param errorEvent sip错误订阅 + */ + @Override + public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + String stream = ssrcInfo.getStream(); + + if (device == null) { + return; + } + + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), + ssrcInfo.getPort()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, + "rtsp", mediaServerItem.getId()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + if (event != null) { + event.response(mediaServerItemInUse, json); + subscribe.removeSubscribe(hookSubscribe); + } + }); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + } else { + sdpIp = mediaServerItem.getSdpIp(); + } + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 " + sdpIp + "\r\n"); + content.append("t=0 0\r\n"); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 +// content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备 + + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + logger.info("=====================请求预览视频===================="+request); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + errorEvent.response(e); + }), e -> { + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 + ResponseEvent responseEvent = (ResponseEvent) e.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play); + okEvent.response(e); + }); + } + + /** + * 请求预览视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param okEvent hook订阅 + * @param errorEvent sip错误订阅 + */ + @Override + public void playStreamCmd1(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent,HookSubscribe.Event event1) throws InvalidArgumentException, SipException, ParseException { + String stream = ssrcInfo.getStream(); + + if (device == null) { + return; + } + + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), + ssrcInfo.getPort()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, + "rtsp", mediaServerItem.getId()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + if (event != null) { + event.response(mediaServerItemInUse, json); + subscribe.removeSubscribe(hookSubscribe); + } + }); + Hook rtpHook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId()); + subscribe1.addSubscribe(rtpHook, (hookData) -> { + if (event1 != null) { + event1.response(hookData); + subscribe1.removeSubscribe(rtpHook); + } + }); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + } else { + sdpIp = mediaServerItem.getSdpIp(); + } + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 " + sdpIp + "\r\n"); + content.append("t=0 0\r\n"); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 + // content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备 + + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + errorEvent.response(e); + }), e -> { + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 + ResponseEvent responseEvent = (ResponseEvent) e.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play); + okEvent.response(e); + }); + } + + /** + * 请求预览视频流 + * + * @param device 视频设备 + */ + @Override + public void inviteVideo(Device device, String channelId, String sdp, MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + Request request = headerProvider.createInviteRequest(device, channelId, sdp, SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),callIdHeader); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, null, event -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play); + okEvent.response(event); + }); + } + + /** + * 语音广播 + * + * @param device 摄像头音频通道 + * @param channelId 预览通道 + */ + @Override + public void audioBroadcastCmd(String type,Device device, String channelId, String sdp, MediaServerItem mediaServerItem,SSRCInfo ssrcInfo, SipSubscribe.Event okEvent)throws InvalidArgumentException, SipException, ParseException { + //方案一,采用sdp消息方式 + if(type.equals("1")){ + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + Request request = headerProvider.createInviteRequest(device, channelId, sdp, SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),callIdHeader); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, null, event -> { + okEvent.response(event); + }); + }else{//方案二,采用XML通知消息方式 + StringBuffer broadcastXml = new StringBuffer(200); + String charset = device.getCharset(); + broadcastXml.append("\r\n"); + broadcastXml.append("\r\n"); + broadcastXml.append("Broadcast\r\n"); + broadcastXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + broadcastXml.append("" + sipConfig.getId() + "\r\n"); + broadcastXml.append("" + device.getDeviceId() + "\r\n"); + broadcastXml.append("\r\n"); + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,callIdHeader); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, null,event -> { + okEvent.response(event); + } + ); + + } + + } + + + /** + * 请求回放视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + @Override + public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, + SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort()); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + }else { + sdpIp = mediaServerItem.getSdpIp(); + } + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("s=Playback\r\n"); + content.append("u=" + channelId + ":0\r\n"); + content.append("c=IN IP4 " + sdpIp + "\r\n"); + content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " " + + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n"); + + String streamMode = device.getStreamMode(); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { + // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { + // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + + //ssrc + content.append("y=" + ssrcInfo.getSsrc() + "\r\n"); + + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + // 添加订阅 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + if (hookEvent != null) { + InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()); + hookEvent.call(inviteStreamInfo); + } + subscribe.removeSubscribe(hookSubscribe); + }); + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc()); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.playback); + okEvent.response(event); + }); + if (inviteStreamCallback != null) { + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream())); + } + } + + /** + * 请求历史媒体下载 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param downloadSpeed 下载倍速参数 + */ + @Override + public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, int downloadSpeed, + InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort()); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + }else { + sdpIp = mediaServerItem.getSdpIp(); + } + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("s=Download\r\n"); + content.append("u=" + channelId + ":0\r\n"); + content.append("c=IN IP4 " + sdpIp + "\r\n"); + content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " " + + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n"); + + String streamMode = device.getStreamMode().toUpperCase(); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); + content.append("a=fmtp:99 profile-level-id=3\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + content.append("a=downloadspeed:" + downloadSpeed + "\r\n"); + + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc + logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId()); + // 添加订阅 + CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport()); + String callId= newCallIdHeader.getCallId(); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + logger.debug("sipc 添加订阅===callId {}",callId); + hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream())); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("regist", false); + hookSubscribe.getContent().put("schema", "rtsp"); + // 添加流注销的订阅,注销了后向设备发送bye + subscribe.addSubscribe(hookSubscribe, + (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> { + logger.info("[录像]下载结束, 发送BYE"); + try { + streamByeCmd(device, channelId, ssrcInfo.getStream(), callId); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage()); + } + }); + }); + + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc()); + if (inviteStreamCallback != null) { + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,callId, "rtp", ssrcInfo.getStream())); + } + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + String contentString =new String(response.getRawContent()); + int ssrcIndex = contentString.indexOf("y="); + String ssrc=ssrcInfo.getSsrc(); + if (ssrcIndex >= 0) { + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + } + streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download); + okEvent.response(event); + }); + } + + /** + * 视频流停止, 不使用回调 + */ + @Override + public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException { + streamByeCmd(device, channelId, stream, callId, null); + } + + /** + * 视频流停止 + */ + @Override + public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callId, stream); + if (ssrcTransaction == null) { + throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream); + } + + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); + streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo()); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); + } + + + + /** + * 语音广播 + * + * @param device 视频设备 + */ + @Override + public void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer broadcastXml = new StringBuffer(200); + String charset = device.getCharset(); + broadcastXml.append("\r\n"); + broadcastXml.append("\r\n"); + broadcastXml.append("Broadcast\r\n"); + broadcastXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + broadcastXml.append("" + sipConfig.getId() + "\r\n"); + broadcastXml.append("" + device.getDeviceId() + "\r\n"); + broadcastXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + @Override + public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer broadcastXml = new StringBuffer(200); + String charset = device.getCharset(); + broadcastXml.append("\r\n"); + broadcastXml.append("\r\n"); + broadcastXml.append("Broadcast\r\n"); + broadcastXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + broadcastXml.append("" + sipConfig.getId() + "\r\n"); + broadcastXml.append("" + device.getDeviceId() + "\r\n"); + broadcastXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + + } + + + /** + * 音视频录像控制 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param recordCmdStr 录像命令:Record / StopRecord + */ + @Override + public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("" + recordCmdStr + "\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent); + } + + /** + * 远程启动控制命令 + * + * @param device 视频设备 + */ + @Override + public void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("Boot\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + } + + /** + * 报警布防/撤防命令 + * + * @param device 视频设备 + * @param guardCmdStr "SetGuard"/"ResetGuard" + */ + @Override + public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("" + guardCmdStr + "\r\n"); + cmdXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent); + } + + /** + * 报警复位命令 + * + * @param device 视频设备 + */ + @Override + public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("ResetAlarm\r\n"); + if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent); + } + + /** + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + @Override + public void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("Send\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + } + + /** + * 看守位控制命令 + * + * @param device 视频设备 + * @param channelId 通道id,非通道则是设备本身 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + */ + @Override + public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { + cmdXml.append("1\r\n"); + if (NumericUtil.isInteger(resetTime)) { + cmdXml.append("" + resetTime + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + if (NumericUtil.isInteger(presetIndex)) { + cmdXml.append("" + presetIndex + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + } else { + cmdXml.append("0\r\n"); + } + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent); + } + + /** + * 设备配置命令 + * + * @param device 视频设备 + */ + @Override + public void deviceConfigCmd(Device device) { + // TODO Auto-generated method stub + } + + /** + * 设备配置命令:basicParam + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param name 设备/通道名称(可选) + * @param expiration 注册过期时间(可选) + * @param heartBeatInterval 心跳间隔时间(可选) + * @param heartBeatCount 心跳超时次数(可选) + */ + @Override + public void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, + String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceConfig\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + if (!ObjectUtils.isEmpty(name)) { + cmdXml.append("" + name + "\r\n"); + } + if (NumericUtil.isInteger(expiration)) { + if (Integer.valueOf(expiration) > 0) { + cmdXml.append("" + expiration + "\r\n"); + } + } + if (NumericUtil.isInteger(heartBeatInterval)) { + if (Integer.valueOf(heartBeatInterval) > 0) { + cmdXml.append("" + heartBeatInterval + "\r\n"); + } + } + if (NumericUtil.isInteger(heartBeatCount)) { + if (Integer.valueOf(heartBeatCount) > 0) { + cmdXml.append("" + heartBeatCount + "\r\n"); + } + } + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备状态 + * + * @param device 视频设备 + */ + @Override + public void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + String charset = device.getCharset(); + StringBuffer catalogXml = new StringBuffer(200); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("DeviceStatus\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备信息 + * + * @param device 视频设备 + */ + @Override + public void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer catalogXml = new StringBuffer(200); + String charset = device.getCharset(); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("DeviceInfo\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + /** + * 查询目录列表 + * + * @param device 视频设备 + */ + @Override + public void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException { + + StringBuffer catalogXml = new StringBuffer(200); + String charset = device.getCharset(); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append(" Catalog\r\n"); + catalogXml.append(" " + sn + "\r\n"); + catalogXml.append(" " + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询录像信息 + * + * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + @Override + public void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + if (secrecy == null) { + secrecy = 0; + } + if (type == null) { + type = "all"; + } + + StringBuffer recordInfoXml = new StringBuffer(200); + String charset = device.getCharset(); + recordInfoXml.append("\r\n"); + recordInfoXml.append("\r\n"); + recordInfoXml.append("RecordInfo\r\n"); + recordInfoXml.append("" + sn + "\r\n"); + recordInfoXml.append("" + channelId + "\r\n"); + if (startTime != null) { + recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "\r\n"); + } + if (endTime != null) { + recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "\r\n"); + } + if (secrecy != null) { + recordInfoXml.append(" " + secrecy + " \r\n"); + } + if (type != null) { + // 大华NVR要求必须增加一个值为all的文本元素节点Type + recordInfoXml.append("" + type + "\r\n"); + } + recordInfoXml.append("\r\n"); + + + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + } + + /** + * 查询报警信息 + * + * @param device 视频设备 + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @Override + public void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType, + String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Alarm\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + if (!ObjectUtils.isEmpty(startPriority)) { + cmdXml.append("" + startPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(endPriority)) { + cmdXml.append("" + endPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!ObjectUtils.isEmpty(startTime)) { + cmdXml.append("" + startTime + "\r\n"); + } + if (!ObjectUtils.isEmpty(endTime)) { + cmdXml.append("" + endTime + "\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备配置 + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param configType 配置类型: + */ + @Override + public void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("ConfigDownload\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("" + configType + "\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备预置位置 + * + * @param device 视频设备 + */ + @Override + public void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("PresetQuery\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询移动设备位置数据 + * + * @param device 视频设备 + */ + @Override + public void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer mobilePostitionXml = new StringBuffer(200); + String charset = device.getCharset(); + mobilePostitionXml.append("\r\n"); + mobilePostitionXml.append("\r\n"); + mobilePostitionXml.append("MobilePosition\r\n"); + mobilePostitionXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + mobilePostitionXml.append("" + device.getDeviceId() + "\r\n"); + mobilePostitionXml.append("60\r\n"); + mobilePostitionXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + + } + + /** + * 订阅、取消订阅移动位置 + * + * @param device 视频设备 + * @return true = 命令发送成功 + */ + @Override + public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer subscribePostitionXml = new StringBuffer(200); + String charset = device.getCharset(); + subscribePostitionXml.append("\r\n"); + subscribePostitionXml.append("\r\n"); + subscribePostitionXml.append("MobilePosition\r\n"); + subscribePostitionXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + subscribePostitionXml.append("" + device.getDeviceId() + "\r\n"); + if (device.getSubscribeCycleForMobilePosition() > 0) { + subscribePostitionXml.append("" + device.getMobilePositionSubmissionInterval() + "\r\n"); + } + subscribePostitionXml.append("\r\n"); + + CallIdHeader callIdHeader; + + if (requestOld != null) { + callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId()); + } else { + callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + } + SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4)); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + return request; + } + + /** + * 订阅、取消订阅报警信息 + * + * @param device 视频设备 + * @param expires 订阅过期时间(0 = 取消订阅) + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @Override + public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Alarm\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + if (!ObjectUtils.isEmpty(startPriority)) { + cmdXml.append("" + startPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(endPriority)) { + cmdXml.append("" + endPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!ObjectUtils.isEmpty(startTime)) { + cmdXml.append("" + startTime + "\r\n"); + } + if (!ObjectUtils.isEmpty(endTime)) { + cmdXml.append("" + endTime + "\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence",sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + @Override + public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Catalog\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("\r\n"); + + CallIdHeader callIdHeader; + + if (requestOld != null) { + callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId()); + } else { + callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + } + + // 有效时间默认为60秒以上 + SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog", + callIdHeader); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + return request; + } + + @Override + public void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer dragXml = new StringBuffer(200); + String charset = device.getCharset(); + dragXml.append("\r\n"); + dragXml.append("\r\n"); + dragXml.append("DeviceControl\r\n"); + dragXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + dragXml.append("" + device.getDeviceId() + "\r\n"); + } else { + dragXml.append("" + channelId + "\r\n"); + } + dragXml.append(cmdString); + dragXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + logger.debug("拉框信令: " + request.toString()); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + } + + + + + + /** + * 回放暂停 + */ + @Override + public void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException { + StringBuffer content = new StringBuffer(200); + content.append("PAUSE RTSP/1.0\r\n"); + content.append("CSeq: " + getInfoCseq() + "\r\n"); + content.append("PauseTime: now\r\n"); + + playbackControlCmd(device, streamInfo, content.toString(), null, null); + } + + + /** + * 回放恢复 + */ + @Override + public void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException { + StringBuffer content = new StringBuffer(200); + content.append("PLAY RTSP/1.0\r\n"); + content.append("CSeq: " + getInfoCseq() + "\r\n"); + content.append("Range: npt=now-\r\n"); + + playbackControlCmd(device, streamInfo, content.toString(), null, null); + } + + /** + * 回放拖动播放 + */ + @Override + public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException { + StringBuffer content = new StringBuffer(200); + content.append("PLAY RTSP/1.0\r\n"); + content.append("CSeq: " + getInfoCseq() + "\r\n"); + content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n"); + + playbackControlCmd(device, streamInfo, content.toString(), null, null); + } + + /** + * 回放倍速播放 + */ + @Override + public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException { + StringBuffer content = new StringBuffer(200); + content.append("PLAY RTSP/1.0\r\n"); + content.append("CSeq: " + getInfoCseq() + "\r\n"); + content.append("Scale: " + String.format("%.6f", speed) + "\r\n"); + + playbackControlCmd(device, streamInfo, content.toString(), null, null); + } + + private int getInfoCseq() { + return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8)); + } + + @Override + public void playbackControlCmd(Device device, StreamInfo streamInfo, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { + + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), streamInfo.getChannelId(), null, streamInfo.getStream()); + if (ssrcTransaction == null) { + logger.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream()); + return; + } + + SIPRequest request = headerProvider.createInfoRequest(device, streamInfo.getChannelId(), content, ssrcTransaction.getSipTransactionInfo()); + if (request == null) { + logger.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream()); + return; + } + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + } + + @Override + public void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException { + if (device == null) { + return; + } + logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(), + deviceAlarm.getLongitude(), deviceAlarm.getLatitude()); + + String characterSet = device.getCharset(); + StringBuffer deviceStatusXml = new StringBuffer(600); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("Alarm\r\n"); + deviceStatusXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getChannelId() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmPriority() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmMethod() + "\r\n"); + deviceStatusXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmDescription() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getLongitude() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getLatitude() + "\r\n"); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmType() + "\r\n"); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, + sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + @Override + public void streamByeCmdForDeviceInvite(Device device, String channelId, SipTransactionInfo sipTransactionInfo, + SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException + , ParseException, SsrcTransactionNotFoundException { + Request byteRequest = headerProvider.createByteRequestForDeviceInvite(device, channelId, sipTransactionInfo); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); + } + + @Override + public void talkStreamCmd(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem, Device device, + String channelId, String callId, HookSubscribe.Event event, + HookSubscribe.Event eventForPush, SipSubscribe.Event okEvent, + SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, + ParseException { + + String stream = sendRtpItem.getStreamId(); + + if (device == null) { + return; + } + if (!mediaServerItem.isRtpEnable()) { + // 单端口暂不支持语音喊话 + logger.info("[语音喊话] 单端口暂不支持此操作"); + return; + } + + logger.info("[语音喊话] {} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), + sendRtpItem.getPort()); + Hook hook = Hook.getInstance(HookType.on_media_arrival, "rtp", stream, mediaServerItem.getId()); + subscribe1.addSubscribe(hook, (hookData) -> { + if (event != null) { + event.response(hookData); + subscribe1.removeSubscribe(hook); + } + }); + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), + device.getTransport()); + callIdHeader.setCallId(callId); + Hook publishHook = Hook.getInstance(HookType.on_publish, "rtp", stream, mediaServerItem.getId()); + subscribe1.addSubscribe(publishHook, (hookData) -> { + if (eventForPush != null) { + eventForPush.response(hookData); + } + }); + // + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("s=Talk\r\n"); + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("t=0 0\r\n"); + + content.append("m=audio " + sendRtpItem.getPort() + " TCP/RTP/AVP 8\r\n"); + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + content.append("a=sendrecv\r\n"); + content.append("a=rtpmap:8 PCMA/8000\r\n"); + + content.append("y=" + sendRtpItem.getSsrc() + "\r\n");//ssrc + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 + content.append("f=v/////a/1/8/1" + "\r\n"); + + Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sendRtpItem.getSsrc(), callIdHeader); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> { + streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStreamId()); + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + errorEvent.response(e); + }), e -> { + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 + ResponseEvent responseEvent = (ResponseEvent) e.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId, "talk", stream, sendRtpItem.getSsrc(), + mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.TALK); + okEvent.response(e); + }); + } + + /** + * 语音广播 + * + * @param device 视频设备 + */ + @Override + public void audioBroadcastCmd(Device device, String channelId, SipSubscribe.Event okEvent, + SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, + ParseException { + StringBuffer broadcastXml = new StringBuffer(200); + String charset = device.getCharset(); + broadcastXml.append("\r\n"); + broadcastXml.append("\r\n"); + broadcastXml.append("Broadcast\r\n"); + broadcastXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + broadcastXml.append("" + sipConfig.getId() + "\r\n"); + broadcastXml.append("" + channelId + "\r\n"); + broadcastXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, + sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommanderFroNode.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommanderFroNode.java new file mode 100644 index 0000000..e840723 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommanderFroNode.java @@ -0,0 +1,63 @@ +package com.yfd.monitor.gdw2019.transmit.cmd.impl; + +import cn.hutool.core.date.DateUtil; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.bean.SubstationNode; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForNode; +import com.yfd.monitor.gdw2019.transmit.cmd.SIPRequestHeaderPlarformProvider; +import com.yfd.monitor.gdw2019.transmit.cmd.SIPRequestHeaderProvider; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +@Component +@DependsOn("sipLayer") +public class SIPCommanderFroNode implements ISIPCommanderForNode { + + private final Logger logger = LoggerFactory.getLogger(SIPCommanderFroNode.class); + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SIPSender sipSender; + + @Autowired + private SIPRequestHeaderProvider headerProvider; + + @Override + public void sendTaskToNode(SubstationNode stationnode) throws InvalidArgumentException, ParseException, SipException { + StringBuffer sipXml = new StringBuffer(200); + sipXml.append("\r\n"); + sipXml.append("\r\n"); + sipXml.append("TaskMessage\r\n"); + sipXml.append(""+ stationnode.getServerId() +"\r\n"); + sipXml.append(""+ stationnode.getStationGBId() +"\r\n"); + sipXml.append(""+ stationnode.getStationCode() +"\r\n"); + sipXml.append("101\r\n"); + sipXml.append("1\r\n"); + sipXml.append("\r\n"); + sipXml.append("\r\n"); + sipXml.append("\r\n"); + SIPRequest request = (SIPRequest) headerProvider.createSationNodeRequest(stationnode, sipXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(stationnode.getStationIp()),stationnode.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(stationnode.getStationIp()),request); + } +} + + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommanderFroPlatform.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommanderFroPlatform.java new file mode 100644 index 0000000..9153ba7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/cmd/impl/SIPCommanderFroPlatform.java @@ -0,0 +1,857 @@ +package com.yfd.monitor.gdw2019.transmit.cmd.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.NumberUtil; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.gdw2019.transmit.cmd.SIPRequestHeaderPlarformProvider; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.gdw2019.utils.VideoToImageUtil; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.dto.PlatformRegisterInfo; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.HttpUtils; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import gov.nist.javax.sip.message.MessageFactoryImpl; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.header.CallIdHeader; +import javax.sip.header.WWWAuthenticateHeader; +import javax.sip.message.Request; +import java.io.File; +import java.io.FileInputStream; +import java.security.MessageDigest; +import java.text.ParseException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Component +@DependsOn("sipLayer") +public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { + + private final Logger logger = LoggerFactory.getLogger(SIPCommanderFroPlatform.class); + + private int count = 0; + @Autowired + private SIPRequestHeaderPlarformProvider headerProviderPlatformProvider; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private ZLMRTPServerFactory zlmServerFactory; + + @Autowired + private SIPSender sipSender; + + @Autowired + private DeviceChannelMapper channelMapper; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private SIPCommander cmder; + + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Override + public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + register(parentPlatform, null, null, errorEvent, okEvent, true); + } + + @Override + public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, + SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, + ParseException, SipException { + + register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, true); + } + + @Override + public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false); + } + + @Override + public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www, + SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, boolean isRegister) throws SipException, InvalidArgumentException, ParseException { + Request request; + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + if ("22.58.167.120".equals(parentPlatform.getDeviceIp())) { + logger.info("B接口发送信息" + callIdHeader); + } + String fromTag = SipUtils.getNewFromTag(); + String toTag = null; + if (sipTransactionInfo != null) { + if (sipTransactionInfo.getCallId() != null) { + callIdHeader.setCallId(sipTransactionInfo.getCallId()); + } + if (sipTransactionInfo.getFromTag() != null) { + fromTag = sipTransactionInfo.getFromTag(); + } + if (sipTransactionInfo.getToTag() != null) { + toTag = sipTransactionInfo.getToTag(); + } + } + String callIdFromHeader = callIdHeader.getCallId(); + redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister)); + if (www == null) { + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, + redisCatchStorage.getCSEQ(), fromTag, + toTag, callIdHeader, isRegister ? parentPlatform.getExpires() : 0); + // 将 callid 写入缓存, 等注册成功可以更新状态 + + + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (event) -> { + if (event != null) { + logger.info("向上级平台 [ {} ] 注册发生错误: {} ", + parentPlatform.getServerGBId(), + event.msg); + } + redisCatchStorage.delPlatformRegisterInfo(callIdFromHeader); + if (errorEvent != null) { + errorEvent.response(event); + } + }); + + } else { + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister ? parentPlatform.getExpires() : 0); + } + + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent); + } + + @Override + public void sendCatalogNotice(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo) throws InvalidArgumentException, ParseException, SipException { + if (parentPlatform == null) { + return; + } + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + String fromTag = SipUtils.getNewFromTag(); + String toTag = null; + if (sipTransactionInfo != null) { + if (sipTransactionInfo.getCallId() != null) { + callIdHeader.setCallId(sipTransactionInfo.getCallId()); + } + if (sipTransactionInfo.getFromTag() != null) { + fromTag = sipTransactionInfo.getFromTag(); + } + if (sipTransactionInfo.getToTag() != null) { + toTag = sipTransactionInfo.getToTag(); + } + } + + // 准备回复通道信息 + List channels = storager.queryChannelWithPlatform(parentPlatform.getServerGBId()); +// List deviceChannels = channels.subList(0, 4); + +// // 将列表拆分成每次最多10条的子列表 + List> chunkedList = IntStream.range(0, (channels.size() + 3) / 4) + .mapToObj(i -> channels.stream() + .skip(i * 4) + .limit(4) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + + // 逐个发送数据 + for (List deviceChannels : chunkedList) { + StringBuffer catalogXml = new StringBuffer(); + + catalogXml.append("\r\n") + .append("\r\n") + .append("" + parentPlatform.getCatalogId() + "\r\n") + .append("\r\n"); + + if (deviceChannels.size() > 0) { + for (DeviceChannel channel : deviceChannels) { + catalogXml.append("\r\n "); + } + } + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + Request request = headerProviderPlatformProvider.createNoticeRequest2(parentPlatform, catalogXml.toString(), fromTag, SipUtils.getNewViaTag(), toTag, callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); +// break; + } + + } + + /** + * 向上级回复通道信息 + * + * @param Code 查询目录编码 + * @param FromIndex 节点下记录 + * @param ToIndex 节点下记录 + */ + @Override + public String queryCatalog(ParentPlatform parentPlatform, String Code, String FromIndex, String ToIndex, String fromTag) { + // 准备回复通道信息 +// List channels = storager.queryChannelWithCatalog(Code); +// int index = NumberUtil.parseInt(FromIndex); +// int fromIndex = NumberUtil.parseInt(ToIndex); +// List subList = channels.subList(index - 1, Math.min(channels.size(), fromIndex)); +// StringBuffer catalogXml = new StringBuffer(); +// catalogXml.append("\r\n") +// .append("\r\n") +// .append("\r\n"); +// if (subList.size() > 0) { +// int x = 0; +// for (DeviceChannel channel : subList) { +// +// x++; +// catalogXml.append("\r\n "); +// } +// } +// catalogXml.append("\r\n"); +// catalogXml.append("\r\n"); +// return catalogXml.toString(); + + // 准备回复通道信息 + List channels = storager.queryChannelWithCatalog(Code); + int index = NumberUtil.parseInt(FromIndex); + int fromIndex = NumberUtil.parseInt(FromIndex); + List subList = channels.subList(index-1, Math.min(channels.size(), fromIndex)); + StringBuffer catalogXml = new StringBuffer(); + catalogXml.append("\r\n") + .append("\r\n") + .append("\r\n"); + if (subList.size() > 0) { + for (DeviceChannel channel : subList) { + catalogXml.append("\r\n "); + } + } + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + return catalogXml.toString(); + } + + /** + * 查询设备历史报警信息 + * + * @param Code 查询告警地址编码,视频通道 + * @param UserCode 用户地址编码 + * @param Type 告警类型,见订阅行为 + * @param BeginTime 开始时间,格式如 1990-01-01T00:00:00Z + * @param EndTime 结束时间,格式如 1990-01-01T00:00:00Z + * @param Level 告警级别,仅当节点为告警且全网有统一的告 + * 警级别规划时有效,0 表示未定义告警级别 + * @param FromIndex + * @param ToIndex + * @return + */ + @Override + public String queryHistoryAlarm(ParentPlatform parentPlatform, String Code, String UserCode, String Type, + String BeginTime, String EndTime, String Level, String FromIndex, String ToIndex) { + int index = NumberUtil.parseInt(FromIndex); + int size = (NumberUtil.parseInt(FromIndex) - index) + 1; + List> alarms = storager.queryHistoryAlarmlog(Code, UserCode, Type, BeginTime, EndTime, + Level, index, size); + StringBuilder catalogXml = new StringBuilder(); + catalogXml.append("\r\n") + .append("\r\n").append("\r\n"); + if (alarms.size() > 0) { + for (Map deviceAlarm : alarms) { + catalogXml.append("\r\n "); + } + } + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + return catalogXml.toString(); + } + + /** + * 查询设备录像检索 + * + * @param Code 查询告警地址编码,视频通道 + * @param UserCode 用户地址编码 + * @param Type 告警类型,见订阅行为 + * @param beginTime 开始时间,格式如 1990-01-01T00:00:00Z + * @param endTime 结束时间,格式如 1990-01-01T00:00:00Z + * 警级别规划时有效,0 表示未定义告警级别 + * @param FromIndex + * @param ToIndex + * @return + */ + @Override + public String queryDeviceRecord(ParentPlatform parentPlatform, String Code, String UserCode, String Type, + String beginTime, String endTime, String FromIndex, String ToIndex) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s", Code, beginTime, + endTime)); + } + DeferredResult> result = new DeferredResult<>(); + if (!DateUtil.verification(beginTime, DateUtil.formatter)) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + if (!DateUtil.verification(endTime, DateUtil.formatter)) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + List deviceChannels = deviceChannelMapper.queryChannelByChannelId(Code); + if (deviceChannels.size() <= 0) { + return null; + } + DeviceChannel deviceChannel = deviceChannels.get(0); + //根据摄像头id,查询出对应Nvr设备 + Device device = storager.queryNvrDeviceByDeviceId(deviceChannel.getDeviceId(), deviceChannel.getChannelId()); + // 指定超时时间 1分钟30秒 + String uuid = UUID.randomUUID().toString(); + int sn = (int) ((Math.random() * 9 + 1) * 100000); + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn; + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + try { + // customName 为硬盘录像机对应的摄像机通道 + cmder.recordInfoQuery(device, device.getCustomName(), beginTime, endTime, sn, null, null, null, + (eventResult -> { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + })); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, result); + result.onTimeout(() -> { + msg.setData("timeout"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("timeout"); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + RecordInfo recordInfo = BeanUtil.toBean(result.getResult(), RecordInfo.class); + List recordList = recordInfo.getRecordList(); + int index = NumberUtil.parseInt(FromIndex); + int toIndex = Math.min(NumberUtil.parseInt(ToIndex), recordList.size()); + // 使用subList方法获取子列表 + List channels = recordList.subList(index, toIndex); + StringBuffer catalogXml = new StringBuffer(); + catalogXml.append("\r\n") + .append("\r\n") + .append("\r\n"); + if (channels.size() > 0) { + for (RecordItem recordItem : channels) { + String startTime = recordItem.getStartTime(); + String endTime1 = recordItem.getEndTime(); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime startDate = LocalDateTime.parse(startTime, formatter); + // 将LocalDate转换为ZonedDateTime(UTC时区) + ZonedDateTime zonedStartDate = startDate.atZone(ZoneId.of("UTC")); + Instant startInstant = zonedStartDate.toInstant(); + String isoStartInstantStr = startInstant.toString(); + + LocalDateTime endDate = LocalDateTime.parse(endTime1, formatter); + // 将LocalDate转换为ZonedDateTime(UTC时区) + ZonedDateTime zonedEndDate = endDate.atZone(ZoneId.of("UTC")); + Instant endInstant = zonedEndDate.toInstant(); + String isoEndInstantStr = endInstant.toString(); + +// String uuid1 = UUID.randomUUID().toString(); +// String key1 = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceChannel.getDeviceId() + deviceChannel.getChannelId(); +// DeferredResult> result1 = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); +// resultHolder.put(key1, uuid1, result); +// +// WVPResult wvpResult = new WVPResult<>(); +// +// RequestMessage msg1 = new RequestMessage(); +// msg.setKey(key); +// msg.setId(uuid); +// +// +// this.playBack(deviceChannel.getDeviceId(), deviceChannel.getChannelId(), startTime, endTime, null, +// playBackResult->{ +// wvpResult.setCode(playBackResult.getCode()); +// wvpResult.setMsg(playBackResult.getMsg()); +// if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) { +// StreamInfo streamInfo = (StreamInfo)playBackResult.getData(); +// wvpResult.setData(new StreamContent(streamInfo)); +// } +// msg1.setData(wvpResult); +// resultHolder.invokeResult(msg1); +// }); +// try { +// Thread.sleep(2000); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } + + catalogXml.append("\r\n "); + } + } + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + return catalogXml.toString(); + } + + + /** + * 向上级回复DeviceStatus查询信息 + * + * @param parentPlatform 平台信息 + * @param sn + * @param fromTag + * @return + */ + @Override + public void deviceStatusResponse(ParentPlatform parentPlatform, String channelId, String sn, String fromTag, boolean status) throws SipException, InvalidArgumentException, ParseException { + if (parentPlatform == null) { + return; + } + String statusStr = (status) ? "ONLINE" : "OFFLINE"; + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer deviceStatusXml = new StringBuffer(600); + deviceStatusXml.append("\r\n") + .append("\r\n") + .append("DeviceStatus\r\n") + .append("" + sn + "\r\n") + .append("" + channelId + "\r\n") + .append("OK\r\n") + .append("" + statusStr + "\r\n") + .append("OK\r\n") + .append("\r\n"); + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + } + + + @Override + public void sendAlarmMessage(ParentPlatform parentPlatform, List deviceAlarms) throws SipException, InvalidArgumentException, ParseException { + if (parentPlatform == null) { + return; + } + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer alertXml = new StringBuffer(600); + + alertXml.append("\r\n") + .append("\r\n") + .append("\r\n"); + + for (DeviceAlarm deviceAlarm : deviceAlarms) { + alertXml.append("\r\n "); + } + + + alertXml.append("\r\n"); + alertXml.append("\r\n"); + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, alertXml.toString(), SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + + } + + @Override + public void snapImageAndsendNoticeMsg(String deviceid, String PicServer, ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo) throws SipException, InvalidArgumentException, ParseException { + //(1)调用接口进行视频截图,并保存到目录中,同时在redis保存截图信息 + String ffmpegpath = userSetting.getFfmpegpath(); + String channelid = deviceid; + List channels = channelMapper.queryAllChannels(deviceid);//获取一个默认通道 + if (channels.size() > 0) { + channelid = channels.get(0).getChannelId(); + } + String videoUrl = String.format("rtsp://%s:554/rtp/%s_%s", parentPlatform.getDeviceIp(), deviceid, channelid); + // 获取当前日期时间 + LocalDateTime currentDateTime = LocalDateTime.now(); + // 定义日期时间格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"); + // 格式化日期时间 + String formattedDateTime = currentDateTime.format(formatter); + String imagefilename = deviceid + "_" + formattedDateTime + ".jpg"; + VideoToImageUtil.getVideoPicture(ffmpegpath, videoUrl, userSetting.getSnapfilepath() + imagefilename); + // 保存文件信息 + List> items = new ArrayList<>(); + File directory = new File(userSetting.getSnapfilepath()); + if (directory.exists() && directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile()) { + Map imageInfo = new HashMap(); + String itemCode = deviceid; // 你的前端设备地址编码 + String fileType = "0"; // 文件类型,0表示图片 + String time = file.getName().split(".")[0].split("_")[1]; // 解析获取文件时间 + String fileUrl = "http://" + parentPlatform.getDeviceIp() + ":8080/download/" + file.getName(); // 抓拍图片的下载地址 + String fileSize = String.valueOf(file.length()); // 文件大小,单位:字节 + // 计算SHA-256校验和 + String verify = null; + try { + verify = calculateSHA256(file); + } catch (Exception e) { + e.printStackTrace(); + } + imageInfo.put("Item Code", itemCode); + imageInfo.put("Type", fileType); + imageInfo.put("Time", time); + imageInfo.put("FileUrl", fileUrl); + imageInfo.put("FileSize", fileSize); + imageInfo.put("Verify", verify); + items.add(imageInfo); + } + } + } + } + + //(2)调用http接口上传截图文件, + try { + boolean ok = HttpUtils.uploadFile(PicServer, userSetting.getSnapfilepath() + imagefilename); + if (!ok) { + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + + //(3)调用Notice命令发送通知 + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + String fromTag = SipUtils.getNewFromTag(); + String toTag = null; + if (sipTransactionInfo != null) { + if (sipTransactionInfo.getCallId() != null) { + callIdHeader.setCallId(sipTransactionInfo.getCallId()); + } + if (sipTransactionInfo.getFromTag() != null) { + fromTag = sipTransactionInfo.getFromTag(); + } + if (sipTransactionInfo.getToTag() != null) { + toTag = sipTransactionInfo.getToTag(); + } + } + StringBuffer catalogXml = new StringBuffer(); + catalogXml.append("\r\n") + .append("\r\n"); + if (items.size() > 0) { + for (Map item : items) { + catalogXml.append("\r\n "); + } + } + catalogXml.append("\r\n"); + + Request request = headerProviderPlatformProvider.createNoticeRequest2(parentPlatform, catalogXml.toString(), fromTag, SipUtils.getNewViaTag(), toTag, callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + + //(4)文件上传成功后,并发送了通知信息后,删除截图文件 + String filePath = userSetting.getSnapfilepath() + imagefilename; + File backupDir = new File(userSetting.getSnapfilepath(), "backup"); + if (!backupDir.exists() && !backupDir.mkdirs()) { + backupDir.mkdirs(); + } + String backfilePath = userSetting.getSnapfilepath() + "backup/" + imagefilename; + + File fileToDelete = new File(filePath); + if (fileToDelete.exists()) { + FileUtil.copy(filePath, backfilePath, true);//图片拷贝到备份目录下 + if (fileToDelete.delete()) { + System.out.println("File deleted: " + filePath); + } else { + System.out.println("Failed to delete the file: " + filePath); + } + } else { + System.out.println("File not found: " + filePath); + } + + } + + @Override + public void sendNotify(ParentPlatform parentPlatform, + SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) + throws SipException, ParseException, InvalidArgumentException { + MessageFactoryImpl messageFactory = (MessageFactoryImpl) SipFactory.getInstance().createMessageFactory(); + String characterSet = parentPlatform.getCharacterSet(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset(characterSet); + + SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, subscribeInfo); + + sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest, errorEvent, okEvent); + } + + + @Override + public void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException { + if (parentPlatform == null) { + return; + } + logger.info("[国标级联] 发送录像数据通道: {}", recordInfo.getChannelId()); + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer recordXml = new StringBuffer(600); + recordXml.append("\r\n") + .append("\r\n") + .append("RecordInfo\r\n") + .append("" + recordInfo.getSn() + "\r\n") + .append("" + deviceChannel.getChannelId() + "\r\n") + .append("" + recordInfo.getSumNum() + "\r\n"); + if (recordInfo.getRecordList() == null) { + recordXml.append("\r\n"); + } else { + recordXml.append("\r\n"); + if (recordInfo.getRecordList().size() > 0) { + for (RecordItem recordItem : recordInfo.getRecordList()) { + recordXml.append("\r\n"); + if (deviceChannel != null) { + recordXml.append("" + deviceChannel.getChannelId() + "\r\n") + .append("" + recordItem.getName() + "\r\n") + .append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "\r\n") + .append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "\r\n") + .append("" + recordItem.getSecrecy() + "\r\n") + .append("" + recordItem.getType() + "\r\n"); + if (!ObjectUtils.isEmpty(recordItem.getFileSize())) { + recordXml.append("" + recordItem.getFileSize() + "\r\n"); + } + if (!ObjectUtils.isEmpty(recordItem.getFilePath())) { + recordXml.append("" + recordItem.getFilePath() + "\r\n"); + } + } + recordXml.append("\r\n"); + } + } + } + + recordXml.append("\r\n") + .append("\r\n"); + logger.info("[国标级联] 发送录像数据通道:{}, 内容: {}", recordInfo.getChannelId(), recordXml); + // callid + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> { + logger.info("[国标级联] 发送录像数据通道:{}, 发送成功", recordInfo.getChannelId()); + }); + + } + + + @Override + public void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException { + if (platform == null) { + return; + } + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platform.getServerGBId(), null, null, callId); + if (sendRtpItem != null) { + streamByeCmd(platform, sendRtpItem); + } + } + + @Override + public void streamByeCmd(ParentPlatform parentPlatform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { + if (sendRtpItem == null) { + logger.info("[向上级发送BYE], sendRtpItem 为NULL"); + return; + } + if (parentPlatform == null) { + logger.info("[向上级发送BYE], platform 为NULL"); + return; + } + logger.info("[向上级发送BYE], {}/{}", parentPlatform.getServerGBId(), sendRtpItem.getChannelId()); + String mediaServerId = sendRtpItem.getMediaServerId(); + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + zlmServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId()); + } + SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem); + if (byeRequest == null) { + logger.warn("[向上级发送bye]:无法创建 byeRequest"); + } + sipSender.transmitRequest(parentPlatform.getDeviceIp(), byeRequest); + } + + private String calculateSHA256(File file) throws Exception { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] buffer = new byte[8192]; + int bytesRead; + FileInputStream fis = new FileInputStream(file); + + while ((bytesRead = fis.read(buffer)) != -1) { + digest.update(buffer, 0, bytesRead); + } + + fis.close(); + + // Convert the byte to hex format + StringBuilder sb = new StringBuilder(); + for (byte b : digest.digest()) { + sb.append(String.format("%02x", b)); + } + + return sb.toString(); + } + + + @Override + public void sendAlarmNotice(ParentPlatform parentPlatform, List> alarmitems, @Nullable SipTransactionInfo sipTransactionInfo) throws InvalidArgumentException, ParseException, SipException { + if (parentPlatform == null) { + return; + } + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + String fromTag = SipUtils.getNewFromTag(); + String toTag = null; + if (sipTransactionInfo != null) { + if (sipTransactionInfo.getCallId() != null) { + callIdHeader.setCallId(sipTransactionInfo.getCallId()); + } + if (sipTransactionInfo.getFromTag() != null) { + fromTag = sipTransactionInfo.getFromTag(); + } + if (sipTransactionInfo.getToTag() != null) { + toTag = sipTransactionInfo.getToTag(); + } + } + StringBuffer Xml = new StringBuffer(); + Xml.append("\r\n") + .append("\r\n"); + if (alarmitems.size() > 0) { + for (Map item : alarmitems) { + Xml.append("\r\n "); + } + } + Xml.append("\r\n"); + + Request request = headerProviderPlatformProvider.createNoticeRequest2(parentPlatform, Xml.toString(), fromTag, SipUtils.getNewViaTag(), toTag, callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + } + + @Override + public void sendDeviceStatusNotice(ParentPlatform parentPlatform, List> devicestatus, @Nullable SipTransactionInfo sipTransactionInfo) throws InvalidArgumentException, ParseException, SipException { + if (parentPlatform == null) { + return; + } + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(), parentPlatform.getTransport()); + String fromTag = SipUtils.getNewFromTag(); + String toTag = null; + if (sipTransactionInfo != null) { + if (sipTransactionInfo.getCallId() != null) { + callIdHeader.setCallId(sipTransactionInfo.getCallId()); + } + if (sipTransactionInfo.getFromTag() != null) { + fromTag = sipTransactionInfo.getFromTag(); + } + if (sipTransactionInfo.getToTag() != null) { + toTag = sipTransactionInfo.getToTag(); + } + } + StringBuffer Xml = new StringBuffer(); + Xml.append("\r\n") + .append("\r\n"); + if (devicestatus.size() > 0) { + for (Map item : devicestatus) { + Xml.append("\r\n "); + } + } + Xml.append("\r\n"); + + Request request = headerProviderPlatformProvider.createNoticeRequest2(parentPlatform, Xml.toString(), fromTag, SipUtils.getNewViaTag(), toTag, callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/ISIPRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/ISIPRequestProcessor.java new file mode 100644 index 0000000..032f1a4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/ISIPRequestProcessor.java @@ -0,0 +1,13 @@ +package com.yfd.monitor.gdw2019.transmit.event.request; + +import javax.sip.RequestEvent; + +/** + * @description: 对SIP事件进行处理,包括request, response, timeout, ioException, transactionTerminated,dialogTerminated + * @date: 2021年11月5日 15:47 + */ +public interface ISIPRequestProcessor { + + void process(RequestEvent event); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/SIPRequestProcessorParent.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/SIPRequestProcessorParent.java new file mode 100644 index 0000000..0e11610 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/SIPRequestProcessorParent.java @@ -0,0 +1,236 @@ +package com.yfd.monitor.gdw2019.transmit.event.request; + +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.dom.DOMElement; +import org.dom4j.io.SAXReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.sip.*; +import javax.sip.address.Address; +import javax.sip.address.SipURI; +import javax.sip.header.ContentTypeHeader; +import javax.sip.header.ExpiresHeader; +import javax.sip.header.HeaderFactory; +import javax.sip.message.MessageFactory; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.io.ByteArrayInputStream; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @description:处理接收IPCamera发来的SIP协议请求消息 + * @date: 2020年5月3日 下午4:42:22 + */ +public abstract class SIPRequestProcessorParent { + + private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class); + + @Autowired + private SIPSender sipSender; + + public HeaderFactory getHeaderFactory() { + try { + return SipFactory.getInstance().createHeaderFactory(); + } catch (PeerUnavailableException e) { + logger.error("未处理的异常 ", e); + } + return null; + } + + public MessageFactory getMessageFactory() { + try { + return SipFactory.getInstance().createMessageFactory(); + } catch (PeerUnavailableException e) { + logger.error("未处理的异常 ", e); + } + return null; + } + + class ResponseAckExtraParam{ + String content; + ContentTypeHeader contentTypeHeader; + SipURI sipURI; + int expires = -1; + } + + /*** + * 回复状态码 + * 100 trying + * 200 OK + * 400 + * 404 + */ + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode) throws SipException, InvalidArgumentException, ParseException { + return responseAck(sipRequest, statusCode, null); + } + + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException { + return responseAck(sipRequest, statusCode, msg, null); + } + + public SIPResponse responseSubscribeAck(SIPRequest sipRequest, int statusCode,Integer expires) throws SipException, InvalidArgumentException, ParseException { + if (sipRequest.getToHeader().getTag() == null) { + sipRequest.getToHeader().setTag(SipUtils.getNewTag()); + } + SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, sipRequest); + response.setStatusCode(statusCode); + + ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(expires); + response.addHeader(expiresHeader); + + // 发送response + sipSender.transmitRequest(sipRequest.getLocalAddress().getHostAddress(), response); + + return response; + } + + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException { + if (sipRequest.getToHeader().getTag() == null) { + sipRequest.getToHeader().setTag(SipUtils.getNewTag()); + } + SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, sipRequest); + response.setStatusCode(statusCode); + if (msg != null) { + response.setReasonPhrase(msg); + } + + if (responseAckExtraParam != null) { + if (responseAckExtraParam.sipURI != null && sipRequest.getMethod().equals(Request.INVITE)) { + logger.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort()); + Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress( + SipFactory.getInstance().createAddressFactory().createSipURI(responseAckExtraParam.sipURI.getUser(), responseAckExtraParam.sipURI.getHost()+":"+responseAckExtraParam.sipURI.getPort() + )); + response.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + } + if (responseAckExtraParam.contentTypeHeader != null) { + response.setContent(responseAckExtraParam.content, responseAckExtraParam.contentTypeHeader); + } + + if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) { + if (responseAckExtraParam.expires == -1) { + logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); + }else { + ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(responseAckExtraParam.expires); + response.addHeader(expiresHeader); + } + } + }else { + if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) { + logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); + } + } + + // 发送response + sipSender.transmitRequest(sipRequest.getLocalAddress().getHostAddress(), response); + + return response; + } + + /** + * 回复带sdp的200 + */ + public SIPResponse responseSdpAck(SIPRequest request, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException { + + System.out.println("=======================request========================"+request.toString()); + System.out.println("=======================sdp========================"+sdp); + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("application", "sdp"); + + // 兼容国标中的使用编码@域名作为RequestURI的情况 + SipURI sipURI = (SipURI)request.getRequestURI(); + System.out.println("=======================sipURI1========================"+sipURI.toString()); + if (sipURI.getPort() == -1) { + sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+":"+sipURI.getPort()); + } + System.out.println("=======================sipURI2========================"+sipURI.toString()); + ResponseAckExtraParam responseAckExtraParam = new ResponseAckExtraParam(); + responseAckExtraParam.contentTypeHeader = contentTypeHeader; + responseAckExtraParam.content = sdp; + responseAckExtraParam.sipURI = sipURI; + + return responseAck(request, Response.OK, null, responseAckExtraParam); + } + + /** + * 回复带xml的200 + */ + public SIPResponse responseXmlAck(SIPRequest request, String xml, ParentPlatform platform, Integer expires) throws SipException, InvalidArgumentException, ParseException { + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("application", "xml"); + + SipURI sipURI = (SipURI)request.getRequestURI(); + if (sipURI.getPort() == -1) { + sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+":"+platform.getServerPort()); + } + ResponseAckExtraParam responseAckExtraParam = new ResponseAckExtraParam(); + responseAckExtraParam.contentTypeHeader = contentTypeHeader; + responseAckExtraParam.content = xml; + responseAckExtraParam.sipURI = sipURI; + responseAckExtraParam.expires = expires; + return responseAck(request, Response.OK, null, responseAckExtraParam); + } + + public Element getRootElement(RequestEvent evt) throws DocumentException { + return getRootElement(evt, "gb2312"); + } + public Element getRootElement(RequestEvent evt, String charset) throws DocumentException { + if (charset == null) { + charset = "gb2312"; + } + Request request = evt.getRequest(); + SAXReader reader = new SAXReader(); + reader.setEncoding(charset); + // 对海康出现的未转义字符做处理。 + String[] destStrArray = new String[]{"<",">","&","'","""}; + char despChar = '&'; // 或许可扩展兼容其他字符 + byte destBye = (byte) despChar; + List result = new ArrayList<>(); + + String contentString = new String(request.getRawContent()); + if(contentString.contains("xml")){ + byte[] rawContent = request.getRawContent(); + if (rawContent == null) { + return null; + } + for (int i = 0; i < rawContent.length; i++) { + if (rawContent[i] == destBye) { + boolean resul = false; + for (String destStr : destStrArray) { + if (i + destStr.length() <= rawContent.length) { + byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length()); + resul = resul || (Arrays.equals(bytes,destStr.getBytes())); + } + } + if (resul) { + result.add(rawContent[i]); + } + }else { + result.add(rawContent[i]); + } + } + Byte[] bytes = new Byte[0]; + byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes)); + + Document xml = reader.read(new ByteArrayInputStream(bytesResult)); + return xml.getRootElement(); + }else{ //视频回放,模拟构造xml节点 + Element element=new DOMElement("SIP_XML"); + element.addAttribute("EventType","Control_Playback"); + return element; + } + + + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/AckRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/AckRequestProcessor.java new file mode 100644 index 0000000..27e1efb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/AckRequestProcessor.java @@ -0,0 +1,219 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.bean.RequestPushStreamMsg; +import com.yfd.monitor.service.redisMsg.RedisGbPlayMsgListener; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.address.SipURI; +import javax.sip.header.CallIdHeader; +import javax.sip.header.FromHeader; +import javax.sip.header.HeaderAddress; +import javax.sip.header.ToHeader; +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * SIP命令类型: ACK请求 + */ +@Component +public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class); + private final String method = "ACK"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + @Autowired + private IMediaServerService mediaServerService; + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ISIPCommander cmder; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + + + /** + * 处理 ACK请求 + * + * @param evt + */ + @Override + public void process(@NotNull RequestEvent evt) { + System.out.println("[收到ACK]:" + evt.getRequest().toString()); + CallIdHeader callIdHeader = (CallIdHeader) evt.getRequest().getHeader(CallIdHeader.NAME); + + String platformGbId = + ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); + logger.info("[收到ACK]: platformGbId->{}", platformGbId); + logger.error("[收到ACK]: platformGbId->{}", platformGbId); + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformGbId); + // 取消设置的超时任务 + dynamicTask.stop(callIdHeader.getCallId()); + String channelId = + ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); + Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformGbId, channelId); + List deviceChannels = deviceChannelMapper.queryAllChannels(device.getDeviceId()); + if (deviceChannels.size() > 0) { + DeviceChannel deviceChannel = deviceChannels.get(0); + channelId = deviceChannel.getChannelId(); + } + + //TODO 使用UDP/TCP发送RTP数据包 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, + callIdHeader.getCallId()); + System.out.println("==================sendRtpItem==================" + sendRtpItem.toString()); + if (sendRtpItem == null) { + logger.warn("[收到ACK]:未找到通道({})的推流信息", channelId); + logger.error("[收到ACK]:未找到通道({})的推流信息", channelId); + return; + } + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + String streamId = sendRtpItem.getStreamId(); + System.out.println("==================sendRtpItem==================1"); + if (mediaInfo.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } + System.out.println("==================sendRtpItem==================2"); + logger.error("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}", streamId, sendRtpItem.getIp(), sendRtpItem.getPort(), + sendRtpItem.getSsrc()); + System.out.println("==================sendRtpItem==================3"); + Map param = new HashMap<>(12); + param.put("vhost", "__defaultVhost__"); + param.put("app", sendRtpItem.getApp()); + param.put("stream", streamId); + param.put("ssrc", sendRtpItem.getSsrc()); + param.put("dst_url", sendRtpItem.getIp()); + param.put("dst_port", sendRtpItem.getPort()); + param.put("is_udp", is_Udp); + param.put("src_port", sendRtpItem.getLocalPort()); + param.put("pt", sendRtpItem.getPt()); + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); +// param.put("use_ps", "1"); + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); + if (!sendRtpItem.isTcp()) { + // 开启rtcp保活 + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0"); + } + System.out.println("==================sendRtpItem==================4"); + System.out.println("=====================param========================" + param.toString()); + if (mediaInfo == null) { + System.out.println("==================sendRtpItem==================5"); + RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( + sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(), + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(), + sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio()); + redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, jsonObject -> { + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); + }); + } else { + System.out.println("==================sendRtpItem==================6"); + System.out.println("==================sendRtpItem==================7" + sendRtpItem.getLocalPort()); + System.out.println("==================sendRtpItem==================8" + sendRtpItem.isRtcp()); + // 如果是非严格模式,需要关闭端口占用 + if (sendRtpItem.getLocalPort() != 0) { + zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc()); + + } + String videoPath = "/data-space/files/riis500/encoded_h264.h264"; + String destinationIp = "22.58.160.19"; + String rtspStr = "rtsp://192.168.22.1:31554/rtp/" + streamId; + int destinationPort = sendRtpItem.getPort(); + String ffmpegStr="ffmpeg -re -i "+rtspStr+" -vcodec h264 -acodec aac -f rtp_mpegts rtp://"+destinationIp+":"+destinationPort; + +// try { +// RTPStreamer.streamVideo(rtspStr, destinationIp, destinationPort); +// RTPStreamer.sendRtp(ffmpegStr); +// } catch (Exception e) { +// e.printStackTrace(); +// } + JSONObject startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); + System.out.println("==================startSendRtpStreamResult=================" + startSendRtpStreamResult.toString()); + if (startSendRtpStreamResult != null) { + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader); + } + } + } + + private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, + JSONObject jsonObject, Map param, CallIdHeader callIdHeader) { + if (jsonObject == null) { + logger.error("RTP推流失败: 请检查ZLM服务"); + } else if (jsonObject.getInteger("code") == 0) { + logger.error("调用ZLM推流接口, 结果: {}", jsonObject); + logger.error("RTP推流成功[ {}/{} ],{}->{}:{}, ", param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); + } else { + logger.error("RTP推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + if (sendRtpItem.isOnlyAudio()) { + // TODO 可能是语音对讲 + } else { + // 向上级平台 + try { + commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/ByeRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/ByeRequestProcessor.java new file mode 100644 index 0000000..691c43c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/ByeRequestProcessor.java @@ -0,0 +1,164 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.InviteStreamType; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.bean.SsrcTransaction; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.StreamPusher; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.*; +import javax.sip.address.SipURI; +import javax.sip.header.CallIdHeader; +import javax.sip.header.FromHeader; +import javax.sip.header.HeaderAddress; +import javax.sip.header.ToHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; + +/** + * SIP命令类型: BYE请求 + */ +@Component +public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class); + private final String method = "BYE"; + + @Autowired + private ISIPCommander cmder; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理BYE请求 + * @param evt + */ + @Override + public void process(RequestEvent evt) { + + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[回复BYE信息失败],{}", e.getMessage()); + } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, null, null, callIdHeader.getCallId()); + logger.info("[收到bye] {}/{}", platformGbId, channelId); + //简单关闭所有推流 + if (sendRtpItem != null){ + String streamId = sendRtpItem.getStreamId(); + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",sendRtpItem.getApp()); + param.put("stream",sendRtpItem.getStreamId()); + param.put("ssrc",sendRtpItem.getSsrc()); + logger.info("[收到bye] 停止向上级推流:{}", streamId); + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null); + zlmrtpServerFactory.stopSendRtpStream(mediaInfo,param); + int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); + if (totalReaderCount <= 0) { + logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); + if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { + Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); + if (device == null) { + logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); + } + try { + logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, streamId, null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); + } + } +// if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { +// MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, +// sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), +// sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId()); +// redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); +// } + } + } + // 可能是设备主动停止 + Device device = storager.queryVideoDeviceByChannelId(channelId); + if (device != null) { + storager.stopPlay(device.getDeviceId(), channelId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId); + if (streamInfo != null) { + redisCatchStorage.stopPlay(streamInfo); + mediaServerService.closeRTPServer(streamInfo.getMediaServerId(), streamInfo.getStream()); + } + SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); + if (ssrcTransactionForPlay != null){ + if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ + // 释放ssrc + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId()); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc()); + } + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); + } + } + SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null); + if (ssrcTransactionForPlayBack != null) { + // 释放ssrc + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId()); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc()); + } + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream()); + } + } + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/CancelRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/CancelRequestProcessor.java new file mode 100644 index 0000000..76bcaa5 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/CancelRequestProcessor.java @@ -0,0 +1,40 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; + +/** + * SIP命令类型: CANCEL请求 + */ +@Component +public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final String method = "CANCEL"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理CANCEL请求 + * + * @param evt 事件 + */ + @Override + public void process(RequestEvent evt) { + // TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令 + + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/InviteRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/InviteRequestProcessor.java new file mode 100644 index 0000000..a717c95 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/InviteRequestProcessor.java @@ -0,0 +1,1109 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import cn.hutool.core.util.ObjUtil; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.SSRCFactory; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.gdw2019.utils.StreamPusher; +import com.yfd.monitor.media.bean.MediaServer; +import com.yfd.monitor.media.zlm.ZLMMediaListManager; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.HookSubscribeFactory; +import com.yfd.monitor.media.zlm.dto.HookSubscribeForStreamChange; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.*; +import com.yfd.monitor.service.bean.ErrorCallback; +import com.yfd.monitor.service.bean.InviteErrorCode; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.service.redisMsg.RedisGbPlayMsgListener; +import com.yfd.monitor.service.redisMsg.RedisPushStreamResponseListener; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.dto.User; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.DeferredResultEx; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import gov.nist.javax.sdp.TimeDescriptionImpl; +import gov.nist.javax.sdp.fields.TimeField; +import gov.nist.javax.sip.header.Via; +import gov.nist.javax.sip.header.ViaList; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sdp.*; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.time.Instant; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * SIP命令类型: INVITE请求 + */ +@SuppressWarnings("rawtypes") +@Component +public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class); + + private final String method = "INVITE"; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IStreamPushService streamPushService; + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; + + @Autowired + private IPlayService playService; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPSender sipSender; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMMediaListManager mediaListManager; + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + + @Autowired + private StreamPusher streamPusher; + + @Autowired + private IUserService userService; + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private DeferredResultHolder resultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理invite请求 + * + * @param evt 请求消息 + */ + @Override + public void process(RequestEvent evt) { + try { + SIPRequest request = (SIPRequest) evt.getRequest(); + String deviceId = SipUtils.getChannelIdFromRequest(request); + String requesterId = SipUtils.getUserIdFromFromHeader(request); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + logger.error("=================处理invite请求==================" + request.toString()); + if (requesterId == null || deviceId == null) { + logger.info("无法从FromHeader的Address中获取到平台ID,或者通道ID,返回400"); + // 参数不全, 发400,请求错误 + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + } + return; + } + + try { + //回复收到,进行中 + responseAck(request, Response.TRYING); +// responseAck(request, Response.RINGING); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite TRYING: {}", e.getMessage()); + } + Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, deviceId); + List deviceChannels = deviceChannelMapper.queryAllChannels(device.getDeviceId()); + MediaServerItem mediaServerItem = playService.getNewMediaServerItem(device); + + String channelId = ""; + if (deviceChannels.size() > 0) { + DeviceChannel deviceChannel = deviceChannels.get(0); + channelId = deviceChannel.getChannelId(); + } + String streamId = null; + if (mediaServerItem.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } + // SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, + // device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); + try { + + // 解析sdp消息, 使用jainsip 自带的sdp解析方式 + String contentString = new String(request.getRawContent()); + Gb28181Sdp gb28181Sdp = SipUtils.parseSDP(contentString); + logger.error("===========================================================gb28181Sdp" + gb28181Sdp.toString()); + SessionDescription sdp = gb28181Sdp.getBaseSdp(); + logger.error("===========================================================sdp" + sdp.toString()); + String sessionName = sdp.getSessionName().getValue(); + String ssrc; + if (userSetting.getUseCustomSsrcForParentInvite() || gb28181Sdp.getSsrc() == null) { + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 + ssrc = "Play".equalsIgnoreCase(sessionName) ? ssrcFactory.getPlaySsrc(mediaServerItem.getId()) : + ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); + } else { + ssrc = gb28181Sdp.getSsrc(); + } + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); + logger.error("============================================sessionName" + sessionName); + if (sessionName.equals("Play")) { + logger.error("===========================================================收到视频调阅"); + mediaServerItem.setType("zlm"); + String addressStr; + if (StringUtils.isEmpty(platform.getServerIP())) { + addressStr = sdp.getConnection().getAddress(); + } else { + addressStr = platform.getServerIP(); + } + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + //String ip = null; + int port = -1; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); +// if (mediaFormats.contains("96")) { + if (mediaFormats.contains("100")||mediaFormats.contains("108")) { + port = media.getMediaPort(); + //String mediaType = media.getMediaType(); + String protocol = media.getProtocol(); + + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equalsIgnoreCase(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equalsIgnoreCase(setup)) { + tcpActive = true; + } else if ("passive".equalsIgnoreCase(setup)) { + tcpActive = false; + } + } + } + break; + } + } +// ViaList viaHeaders = request.getViaHeaders(); +// for (Via viaHeader : viaHeaders) { +// System.out.println("----------viaHeader------------"+viaHeader.toString()); +// if(viaHeader.getRPort()>0){ +// port=viaHeader.getRPort(); +// } +// } + System.out.println("----------port------------"+port); + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + // 不支持的格式,发415 + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage()); + } + return; + } +// String username = sdp.getOrigin().getUsername(); +// String streamTypeStr = null; +// if (mediaTransmissionTCP) { +// if (tcpActive) { +// streamTypeStr = "TCP-ACTIVE"; +// } else { +// streamTypeStr = "TCP-PASSIVE"; +// } +// } else { +// streamTypeStr = "UDP"; +// } +// SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, +// ssrc, requesterId, device.getDeviceId(), channelId, mediaTransmissionTCP, +// platform.isRtcp()); + SendRtpItem sendRtpItem = mediaServerService.createSendRtpItem(mediaServerItem, addressStr, port, + ssrc, requesterId, device.getDeviceId(), channelId, mediaTransmissionTCP, + platform.isRtcp()); + String ptName="PS"; + int pt=96; + if (contentString.contains("H264")) { + sendRtpItem.setPt(100); + ptName="H264"; + pt=100; + } + if (contentString.contains("H265")) { + pt=108; + sendRtpItem.setPt(108); + ptName="H265"; + } +// sendRtpItem.setRtcp(true); + System.out.println("==================sendRtpItem=================" + sendRtpItem.toString()); + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setRtcp(true); + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setPlayType("Play".equalsIgnoreCase(sessionName) ? InviteStreamType.PLAY : + InviteStreamType.PLAYBACK); + Long startTime = null; + Long stopTime = null; + Instant start = null; + Instant end = null; + if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { + TimeDescriptionImpl timeDescription = + (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); + TimeField startTimeFiled = (TimeField) timeDescription.getTime(); + startTime = startTimeFiled.getStartTime(); + stopTime = startTimeFiled.getStopTime(); + + start = Instant.ofEpochSecond(startTime); + end = Instant.ofEpochSecond(stopTime); + } + Long finalStartTime = startTime; + Long finalStopTime = stopTime; + String finalChannelId3 = channelId; + sendRtpItem.setApp("rtp"); + String finalChannelId6 = channelId; + System.out.println("=====================device.getDeviceId()=======================" + device.getDeviceId() + "===" + channelId); + String finalPtName = ptName; + int finalPt = pt; + playService.play(mediaServerItem, device.getDeviceId(), channelId, (mediaServerItemInUse, response) -> { + + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), finalChannelId6); + MediaServerItem mediaServerItemInUSe = mediaServerService.getOne(streamInfo.getMediaServerId()); + logger.error("[上级Invite]下级已经开始推流。 回复200OK(SDP), {}/{}", streamInfo.getApp(), + streamInfo.getStream()); + // * 0 等待设备推流上来 + // * 1 下级已经推流,等待上级平台回复ack + // * 2 推流中 + sendRtpItem.setStatus(1); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + String sdpIp = mediaServerItemInUSe.getSdpIp(); + if (!ObjectUtils.isEmpty(platform.getServerIP())) { + sdpIp = platform.getServerIP(); + } + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=- 0 0 IN IP4 " + sdpIp + "\r\n"); + content.append("s=" + sessionName + "\r\n"); + content.append("c=IN IP4 " + sdpIp + "\r\n"); +// if ("Playback".equalsIgnoreCase(sessionName)) { +// content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n"); +// } else { +// content.append("t=0 0\r\n"); +// } + int localPort = sendRtpItem.getLocalPort(); + if (localPort == 0) { + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 + localPort = new Random().nextInt(65535) + 1; + } + if (sendRtpItem.isTcp()) { + content.append("m=video " + localPort + " TCP/RTP/AVP 96\r\n"); + if (!sendRtpItem.isTcpActive()) { + content.append("a=setup:active\r\n"); + } else { + content.append("a=setup:passive\r\n"); + } + } else { + content.append("m=video " + localPort + " RTP/AVP "+ finalPt +"\r\n"); + } + + content.append("a=mime:\r\n"); + content.append("a=rtpmap:"+ finalPt +" "+ finalPtName +"/90000\r\n"); +// content.append("a=fmtp:100 CIF=1\r\n"); + content.append("a=fmtp:"+ finalPt +" profile-level-id=420028;sprop-parameter-sets=Z0IAKOkBQHsg,aM44gA==\r\n"); + content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); + content.append("a=sendonly\r\n"); + try { + // 超时未收到Ack应该回复bye,当前等待时间为10秒 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("Ack 等待超时"); + mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), sendRtpItem.getSsrc()); + // 回复bye + try { + cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + }, 60 * 1000); + responseSdpAck(request, content.toString(), platform); + // tcp主动模式,回复sdp后开启监听 + if (sendRtpItem.isTcpActive()) { + MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + try { + mediaServerService.startSendRtpPassive(mediaServer, platform, sendRtpItem, 5); + } catch (ControllerException e) { + } + } + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 回复SdpAck", e); + } + + }, event -> { + + logger.error(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg)); + }, null); + } else if (sessionName.equals("Talk")) {//播放语言广播 + SSRCInfo ssrcInfo = null; + try { + responseSdpAck(request, contentString, platform); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + //发起语音广播 + String parent_rtsp = sdp.getURI().toString().replaceAll("\r\n", ""); //推流到上级单位的地址 + String finalStreamId = streamId; + String finalChannelId = channelId; + cmder.audioBroadcastCmd("2", device, channelId, contentString, mediaServerItem, ssrcInfo, (event) -> { + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setPlatformId(platform.getServerGBId()); + sendRtpItem.setMediaServerId(mediaServerItem.getId()); + sendRtpItem.setChannelId(finalChannelId); + sendRtpItem.setStreamId(finalStreamId); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setDeviceId(device.getDeviceId()); + sendRtpItem.setPlayType(InviteStreamType.TALK); + sendRtpItem.setCallId(callIdHeader.getCallId()); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + String ffmpeg_path = userSetting.getFfmpegpath(); + String video_rtsp = String.format("rtsp://%s:554/rtp/%s_%s", platform.getDeviceIp(), device.getDeviceId(), finalChannelId); + streamPusher.pushStreamToParent(ffmpeg_path, video_rtsp, parent_rtsp); + + }); + } else if (sessionName.equals("Playback")) {//录像回放 + SSRCInfo ssrcInfo = null; + try { + responseSdpAck(request, contentString, platform); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + //发起语音广播 + String parent_rtsp = sdp.getURI().toString().replaceAll("\r\n", ""); //推流到上级单位的地址 + String finalStreamId = streamId; + String finalChannelId2 = channelId; + cmder.audioBroadcastCmd("2", device, channelId, contentString, mediaServerItem, ssrcInfo, (event) -> { + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setPlatformId(platform.getServerGBId()); + sendRtpItem.setMediaServerId(mediaServerItem.getId()); + sendRtpItem.setChannelId(finalChannelId2); + sendRtpItem.setStreamId(finalStreamId); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setDeviceId(device.getDeviceId()); + sendRtpItem.setPlayType(InviteStreamType.TALK); + sendRtpItem.setCallId(callIdHeader.getCallId()); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + String ffmpeg_path = userSetting.getFfmpegpath(); + String video_rtsp = String.format("rtsp://%s:554/rtp/%s_%s", platform.getDeviceIp(), device.getDeviceId(), finalChannelId2); + streamPusher.pushStreamToParent(ffmpeg_path, video_rtsp, parent_rtsp); + + }); + } else { + //发起录像回放 + Long startTime = null; + Long stopTime = null; + Instant start = null; + Instant end = null; + if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { + TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); + TimeField startTimeFiled = (TimeField) timeDescription.getTime(); + startTime = startTimeFiled.getStartTime(); + stopTime = startTimeFiled.getStopTime(); + start = Instant.ofEpochSecond(startTime); + end = Instant.ofEpochSecond(stopTime); + } + SipSubscribe.Event errorEvent = ((event) -> { + // 未知错误。直接转发设备点播的错误 + try { + Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } catch (ParseException | SipException e) { + logger.error("未处理的异常 ", e); + } + }); + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + //String ip = null; + int port = -1; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("100")) { + port = media.getMediaPort(); + //String mediaType = media.getMediaType(); + String protocol = media.getProtocol(); + + // 区分TCP发流还是udp, 当前默认udp + if ("RTP/AVP/TCP".equalsIgnoreCase(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equalsIgnoreCase(setup)) { + tcpActive = true; + } else if ("passive".equalsIgnoreCase(setup)) { + tcpActive = false; + } + } + } + break; + } + } + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + // 不支持的格式,发415 + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage()); + } + return; + } + String addressStr = sdp.getConnection().getAddress(); + + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); + + Long finalStartTime = startTime; + Long finalStopTime = stopTime; + boolean finalMediaTransmissionTCP = mediaTransmissionTCP; + String finalChannelId4 = channelId; + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON) -> { + String app = responseJSON.getString("app"); + String stream = responseJSON.getString("stream"); + logger.info("[上级点播]下级已经开始推流。 回复200OK(SDP), {}/{}", app, stream); + // * 0 等待设备推流上来 + // * 1 下级已经推流,等待上级平台回复ack + // * 2 推流中 + sendRtpItem.setStatus(1); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + finalChannelId4 + " 0 0 IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n"); + content.append("s=Playback\r\n"); + content.append("u=" + sendRtpItem.getIp() + ":" + sendRtpItem.getPort() + "/" + sendRtpItem.getApp() + "/" + sendRtpItem.getStreamId() + "\r\n"); + content.append("c=IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n"); + content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n"); + int localPort = sendRtpItem.getLocalPort(); + if (localPort == 0) { + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 + localPort = new Random().nextInt(65535) + 1; + } + content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); + content.append("a=rtpmap:100 H264/90000\r\n"); + content.append("a=fmtp:100 profile-level-id=420028;sprop-parameter-sets=Z0IAKOkBQHsg,aM44gA==\r\n"); + if (finalMediaTransmissionTCP) { + content.append("m=video " + localPort + " RTP/AVP/TCP 100\r\n"); + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } else { + content.append("m=video " + localPort + " RTP/AVP 100\r\n"); + } + + try { + // 超时未收到Ack应该回复bye,当前等待时间为10秒 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("Ack 等待超时"); + mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), sendRtpItem.getSsrc()); + // 回复bye + try { + cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + }, 60 * 1000); + responseSdpAck(request, content.toString(), platform); + + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 回复SdpAck", e); + } + }; + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, + device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + String finalChannelId1 = channelId; + playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, + DateUtil.formatter.format(start), + DateUtil.formatter.format(end), null, result -> { + if (result.getCode() != 0) { + logger.warn("录像回放失败"); + if (result.getEvent() != null) { + errorEvent.response(result.getEvent()); + } + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), finalChannelId1, callIdHeader.getCallId(), null); + try { + responseAck(request, Response.REQUEST_TIMEOUT); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像回放 发送REQUEST_TIMEOUT: {}", e.getMessage()); + } + } else { + if (result.getMediaServerItem() != null) { + hookEvent.response(result.getMediaServerItem(), result.getResponse()); + } + } + }); + } + + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (SipException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + + } catch (Exception e) { + e.printStackTrace(); + logger.error("系统转发错误!", e); + } + + } + + private static String extractHeader(String sipMessage, String headerName) { + String[] headers = sipMessage.split("\r\n"); + for (String header : headers) { + if (header.trim().startsWith(headerName)) { + return header.trim(); + } + } + return null; + } + + private static String extractTagFromFromHeader(String fromHeader) { + Pattern pattern = Pattern.compile(";tag=([^;]+)"); + Matcher matcher = pattern.matcher(fromHeader); + if (matcher.find()) { + return matcher.group(1); + } + return null; + } + + /** + * 安排推流 + */ + private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), + gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, + requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setFromTag(request.getFromTag()); + + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + } + + } + + private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + // 推流 + if (streamPushItem.isSelf()) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + } else { + // 不在线 拉起 + notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + + } else { + // 其他平台内容 + otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + + /** + * 通知流上线 + */ + private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + if ("proxy".equals(gbStream.getStreamType())) { + // TODO 控制启用以使设备上线 + logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream()); + // 监听流上线 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId()); + zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, responseJSON) -> { + String app = responseJSON.getString("app"); + String stream = responseJSON.getString("stream"); + logger.info("[上级点播]拉流代理已经就绪, {}/{}", app, stream); + dynamicTask.stop(callIdHeader.getCallId()); + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }); + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream()); + zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); + }, userSetting.getPlatformPlayTimeout()); + boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream()); + if (!start) { + try { + responseAck(request, Response.BUSY_HERE, "channel [" + gbStream.getGbId() + "] offline"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + } + zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); + dynamicTask.stop(callIdHeader.getCallId()); + } + + + } else if ("push".equals(gbStream.getStreamType())) { + if (!platform.isStartOfflinePush()) { + // 平台设置中关闭了拉起离线的推流则直接回复 + try { + logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream()); + responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + } + return; + } + // 发送redis消息以使设备上线 + logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream()); + + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, + gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(), + platform.getName(), null, gbStream.getMediaServerId()); + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); + // 设置超时 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream()); + try { + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); + responseAck(request, Response.REQUEST_TIMEOUT); // 超时 + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + }, userSetting.getPlatformPlayTimeout()); + // 添加监听 + int finalPort = port; + Boolean finalTcpActive = tcpActive; + + // 添加在本机上线的通知 + mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> { + dynamicTask.stop(callIdHeader.getCallId()); + if (serverId.equals(userSetting.getServerId())) { + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + return; + } + if (finalTcpActive != null) { + sendRtpItem.setTcpActive(finalTcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + } else { + // 其他平台内容 + otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + }); + + // 添加回复的拒绝或者错误的通知 + redisPushStreamResponseListener.addEvent(gbStream.getApp(), gbStream.getStream(), response -> { + if (response.getCode() != 0) { + dynamicTask.stop(callIdHeader.getCallId()); + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); + try { + responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 点播回复: {}", e.getMessage()); + } + } + }); + } + } + + /** + * 来自其他wvp的推流 + */ + private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + logger.info("[级联点播]直播流来自其他平台,发送redis消息"); + // 发送redis消息 + redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(), + streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId, + channelId, mediaTransmissionTCP, platform.isRtcp(), null, responseSendItemMsg -> { + SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem(); + if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + return; + } + // 收到sendItem + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + }, (wvpResult) -> { + + // 错误 + if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) { + // 离线 + // 查询是否在本机上线了 + StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream()); + if (currentStreamPushItem.isPushIng()) { + // 在线状态 + pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + + } else { + // 不在线 拉起 + notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + try { + responseAck(request, Response.BUSY_HERE); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 点播回复 BUSY_HERE: {}", e.getMessage()); + } + }); + } + + public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) { + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + sendRtpItem.getChannelId() + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("t=0 0\r\n"); + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 + int localPort = sendRtpItem.getLocalPort(); + if (localPort == 0) { + localPort = new Random().nextInt(65535) + 1; + } + content.append("m=video " + localPort + " RTP/AVP 96\r\n"); + content.append("a=sendonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + if (sendRtpItem.isTcp()) { + content.append("a=connection:new\r\n"); + if (!sendRtpItem.isTcpActive()) { + content.append("a=setup:active\r\n"); + } else { + content.append("a=setup:passive\r\n"); + } + } + content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); + content.append("f=\r\n"); + + try { + return responseSdpAck(request, content.toString(), platform); + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + return null; + } + + public void inviteFromDeviceHandle(SIPRequest request, String requesterId) { + + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) + Device device = redisCatchStorage.getDevice(requesterId); + if (device != null) { + logger.info("收到设备" + requesterId + "的语音广播Invite请求"); + try { + responseAck(request, Response.TRYING); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + } + String contentString = new String(request.getRawContent()); + // jainSip不支持y=字段, 移除移除以解析。 + String substring = contentString; + String ssrc = "0000000404"; + int ssrcIndex = contentString.indexOf("y="); + if (ssrcIndex > 0) { + substring = contentString.substring(0, ssrcIndex); + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + } + ssrcIndex = substring.indexOf("f="); + if (ssrcIndex > 0) { + substring = contentString.substring(0, ssrcIndex); + } + SessionDescription sdp = null; + try { + sdp = SdpFactory.getInstance().createSessionDescription(substring); + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + int port = -1; + //boolean recvonly = false; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (int i = 0; i < mediaDescriptions.size(); i++) { + MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i); + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("8")) { + port = media.getMediaPort(); + String protocol = media.getProtocol(); + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equals(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equals(setup)) { + tcpActive = true; + } else if ("passive".equals(setup)) { + tcpActive = false; + } + } + } + break; + } + } + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的媒体格式,返回415, {}", e.getMessage()); + } + return; + } + String username = sdp.getOrigin().getUsername(); + String addressStr = sdp.getConnection().getAddress(); + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); + } catch (SdpException e) { + logger.error("[SDP解析异常]", e); + } + + + } else { + logger.warn("来自无效设备/平台的请求"); + try { + responseAck(request, Response.BAD_REQUEST);// 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage()); + } + } + } + + private boolean pushStreamToParent(String from_rtsp, String parent_rtsp) { + //todo : ffmpeg -rtsp_transport tcp -i rtsp://192.168.1.13:554/rtp/34020000001320000001_34020000001320000009 -c:v copy -c:a copy -f rtsp rtsp://192.168.1.13:554/live/232323?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc + return true; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/InviteRequestProcessor1.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/InviteRequestProcessor1.java new file mode 100644 index 0000000..f3ecacf --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/InviteRequestProcessor1.java @@ -0,0 +1,956 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.SSRCFactory; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.media.zlm.ZLMMediaListManager; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.*; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.service.redisMsg.RedisGbPlayMsgListener; +import com.yfd.monitor.service.redisMsg.RedisPushStreamResponseListener; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import gov.nist.javax.sdp.TimeDescriptionImpl; +import gov.nist.javax.sdp.fields.TimeField; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sdp.*; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.time.Instant; +import java.util.Random; +import java.util.Vector; + +/** + * SIP命令类型: INVITE请求(备份) + */ +@SuppressWarnings("rawtypes") +@Component +public class InviteRequestProcessor1 extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor1.class); + + private final String method = "INVITE1"; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IStreamPushService streamPushService; + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; + + @Autowired + private IPlayService playService; + + @Autowired + private SIPSender sipSender; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMMediaListManager mediaListManager; + + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理invite请求主动把流推送到上级系统的流媒体服务器(------需要上级系统的地址,端口-------------------) + * + * @param evt 请求消息 + */ + @Override + public void process(RequestEvent evt) { + // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令 + //ffmpeg -f dshow -i video="YourCameraDeviceName" -vf "format=yuv420p" -vcodec rawvideo -pix_fmt uyvy422 -f sdl "YourWindowTitle" + + try { + SIPRequest request = (SIPRequest)evt.getRequest(); + String channelId = SipUtils.getChannelIdFromRequest(request); + String requesterId = SipUtils.getUserIdFromFromHeader(request); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + //通道 + if (requesterId == null || channelId == null) { + logger.info("无法从FromHeader的Address中获取到平台id,返回400"); + // 参数不全, 发400,请求错误 + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + } + return; + } + + + // 查询请求是否来自上级平台\设备 + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); + if (platform == null) { + inviteFromDeviceHandle(request, requesterId); + + } else { + // 查询平台下是否有该通道 + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); + GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); + PlatformCatalog catalog = storager.getCatalog(channelId); + + MediaServerItem mediaServerItem = null; + StreamPushItem streamPushItem = null; + StreamProxyItem proxyByAppAndStream =null; + // 不是通道可能是直播流 + if (channel != null && gbStream == null) { + // 通道存在,发100,TRYING + try { + responseAck(request, Response.TRYING); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite TRYING: {}", e.getMessage()); + } + } else if (channel == null && gbStream != null) { + + String mediaServerId = gbStream.getMediaServerId(); + mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + if ("proxy".equals(gbStream.getStreamType())) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } else { + streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); + if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } + } + } else { + if ("push".equals(gbStream.getStreamType())) { + streamPushItem = streamPushService.getPush(gbStream.getApp(), gbStream.getStream()); + if (streamPushItem == null) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } + }else if("proxy".equals(gbStream.getStreamType())){ + proxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(gbStream.getApp(), gbStream.getStream()); + if (proxyByAppAndStream == null) { + logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId); + try { + responseAck(request, Response.GONE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite GONE: {}", e.getMessage()); + } + return; + } + } + } + try { + responseAck(request, Response.CALL_IS_BEING_FORWARDED); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite CALL_IS_BEING_FORWARDED: {}", e.getMessage()); + } + } else if (catalog != null) { + try { + // 目录不支持点播 + responseAck(request, Response.BAD_REQUEST, "catalog channel can not play"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 目录不支持点播: {}", e.getMessage()); + } + return; + } else { + logger.info("通道不存在,返回404: {}", channelId); + try { + // 通道不存在,发404,资源不存在 + responseAck(request, Response.NOT_FOUND); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道不存在: {}", e.getMessage()); + } + return; + } + // 解析sdp消息, 使用jainsip 自带的sdp解析方式 + String contentString = new String(request.getRawContent()); + + // jainSip不支持y=字段, 移除以解析。 + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + String ssrcDefault = "0000000000"; + String ssrc; + SessionDescription sdp; + if (ssrcIndex >= 0) { + //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + String substring = contentString.substring(0, contentString.indexOf("y=")); + sdp = SdpFactory.getInstance().createSessionDescription(substring); + } else { + ssrc = ssrcDefault; + sdp = SdpFactory.getInstance().createSessionDescription(contentString); + } + String sessionName = sdp.getSessionName().getValue(); + + Long startTime = null; + Long stopTime = null; + Instant start = null; + Instant end = null; + if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { + TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); + TimeField startTimeFiled = (TimeField) timeDescription.getTime(); + startTime = startTimeFiled.getStartTime(); + stopTime = startTimeFiled.getStopTime(); + + start = Instant.ofEpochSecond(startTime); + end = Instant.ofEpochSecond(stopTime); + } + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + //String ip = null; + int port = -1; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("100")) { + port = media.getMediaPort(); + //String mediaType = media.getMediaType(); + String protocol = media.getProtocol(); + + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equalsIgnoreCase(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equalsIgnoreCase(setup)) { + tcpActive = true; + } else if ("passive".equalsIgnoreCase(setup)) { + tcpActive = false; + } + } + } + break; + } + } + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + // 不支持的格式,发415 + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage()); + } + return; + } + String username = sdp.getOrigin().getUsername(); + String addressStr = sdp.getConnection().getAddress(); + + logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc); + Device device = null; + // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标 + if (channel != null) { + device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); + if (device == null) { + logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel); + try { + responseAck(request, Response.SERVER_INTERNAL_ERROR); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 未找到设备信息: {}", e.getMessage()); + } + return; + } + mediaServerItem = playService.getNewMediaServerItem(device); + if (mediaServerItem == null) { + logger.warn("未找到可用的zlm"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BUSY_HERE: {}", e.getMessage()); + } + return; + } + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setPlayType("Play".equalsIgnoreCase(sessionName) ? InviteStreamType.PLAY : InviteStreamType.PLAYBACK); + + Long finalStartTime = startTime; + Long finalStopTime = stopTime; + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON) -> { + String app = responseJSON.getString("app"); + String stream = responseJSON.getString("stream"); + logger.info("[上级点播]下级已经开始推流。 回复200OK(SDP), {}/{}", app, stream); + // * 0 等待设备推流上来 + // * 1 下级已经推流,等待上级平台回复ack + // * 2 推流中 + sendRtpItem.setStatus(1); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n"); + content.append("s=" + sessionName + "\r\n"); + content.append("c=IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n"); + if ("Playback".equalsIgnoreCase(sessionName)) { + content.append("t=" + finalStartTime + " " + finalStopTime + "\r\n"); + } else { + content.append("t=0 0\r\n"); + } + int localPort = sendRtpItem.getLocalPort(); + if (localPort == 0) { + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 + localPort = new Random().nextInt(65535) + 1; + } + content.append("m=video " + localPort + " RTP/AVP 100\r\n"); + content.append("a=sendonly\r\n"); + content.append("a=rtpmap:100 H264/90000\r\n"); + content.append("a=fmtp:100 CIF=1\r\n"); + content.append("a=fmtp:100 profile-level-id="+111111+";sprop-parameter-sets="+22222+"\r\n"); + content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); + content.append("f=\r\n"); + + try { + // 超时未收到Ack应该回复bye,当前等待时间为10秒 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("Ack 等待超时"); + mediaServerService.releaseSsrc(mediaServerItemInUSe.getId(), sendRtpItem.getSsrc()); + // 回复bye + try { + cmderFroPlatform.streamByeCmd(platform, callIdHeader.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + }, 60 * 1000); + responseSdpAck(request, content.toString(), platform); + + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 回复SdpAck", e); + } + }; + SipSubscribe.Event errorEvent = ((event) -> { + // 未知错误。直接转发设备点播的错误 + try { + Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } catch (ParseException | SipException e) { + logger.error("未处理的异常 ", e); + } + }); + sendRtpItem.setApp("rtp"); + if ("Playback".equalsIgnoreCase(sessionName)) { + sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + sendRtpItem.setStreamId(ssrcInfo.getStream()); + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), + DateUtil.formatter.format(end), null, result -> { + if (result.getCode() != 0) { + logger.warn("录像回放失败"); + if (result.getEvent() != null) { + errorEvent.response(result.getEvent()); + } + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + try { + responseAck(request, Response.REQUEST_TIMEOUT); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像回放 发送REQUEST_TIMEOUT: {}", e.getMessage()); + } + } else { + if (result.getMediaServerItem() != null) { + hookEvent.response(result.getMediaServerItem(), result.getResponse()); + } + } + }); + } else { + sendRtpItem.setPlayType(InviteStreamType.PLAY); + SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); + if (playTransaction != null) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream()); + if (!streamReady) { + boolean hasRtpServer = mediaServerService.checkRtpServer(mediaServerItem, "rtp", playTransaction.getStream()); + if (hasRtpServer) { + logger.info("[上级点播]已经开启rtpServer但是尚未收到流,开启监听流的到来"); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", playTransaction.getStream(), true, "rtsp", mediaServerItem.getId()); + zlmHttpHookSubscribe.addSubscribe(hookSubscribe, hookEvent); + }else { + playTransaction = null; + } + } + } + if (playTransaction == null) { + String streamId = null; + if (mediaServerItem.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); + logger.info(JSONObject.toJSONString(ssrcInfo)); + sendRtpItem.setStreamId(ssrcInfo.getStream()); + sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc); + + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + MediaServerItem finalMediaServerItem = mediaServerItem; + playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> { + logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + }); + } else { + // 当前系统作为下级平台使用,当上级平台点播时不携带ssrc时,并且设备在当前系统中已经点播了。这个时候需要重新给生成一个ssrc,不使用默认的"0000000000"。 + if (ssrc.equals(ssrcDefault)) { + ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); + sendRtpItem.setSsrc(ssrc); + } + + sendRtpItem.setStreamId(playTransaction.getStream()); + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("app", sendRtpItem.getApp()); + jsonObject.put("stream", sendRtpItem.getStreamId()); + hookEvent.response(mediaServerItem, jsonObject); + } + } + } else if (gbStream != null) { + if(ssrc.equals(ssrcDefault)) + { + ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); + } + if("push".equals(gbStream.getStreamType())) { + if (streamPushItem != null && streamPushItem.isPushIng()) { + // 推流状态 + pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } else { + // 未推流 拉起 + notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + }else if ("proxy".equals(gbStream.getStreamType())){ + if (null != proxyByAppAndStream) { + if(proxyByAppAndStream.isStatus()){ + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }else{ + //开启代理拉流 + notifyStreamOnline(evt, request,gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + + + } + } + } + } catch (SdpParseException e) { + logger.error("sdp解析错误", e); + } catch (SdpException e) { + logger.error("未处理的异常 ", e); + } + } + + /** + * 安排推流 + */ + private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setFromTag(request.getFromTag()); + + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + } + + } + private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + // 推流 + if (streamPushItem.isSelf()) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + } else { + // 不在线 拉起 + notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + + } else { + // 其他平台内容 + otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + /** + * 通知流上线 + */ + private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + if ("proxy".equals(gbStream.getStreamType())) { + // TODO 控制启用以使设备上线 + logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream()); + // 监听流上线 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId()); + zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, responseJSON) -> { + String app = responseJSON.getString("app"); + String stream = responseJSON.getString("stream"); + logger.info("[上级点播]拉流代理已经就绪, {}/{}", app, stream); + dynamicTask.stop(callIdHeader.getCallId()); + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }); + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream()); + zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); + }, userSetting.getPlatformPlayTimeout()); + boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream()); + if (!start) { + try { + responseAck(request, Response.BUSY_HERE, "channel [" + gbStream.getGbId() + "] offline"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + } + zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); + dynamicTask.stop(callIdHeader.getCallId()); + } + + + + } else if ("push".equals(gbStream.getStreamType())) { + if (!platform.isStartOfflinePush()) { + // 平台设置中关闭了拉起离线的推流则直接回复 + try { + logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream()); + responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + } + return; + } + // 发送redis消息以使设备上线 + logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream()); + + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, + gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(), + platform.getName(), null, gbStream.getMediaServerId()); + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); + // 设置超时 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream()); + try { + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); + responseAck(request, Response.REQUEST_TIMEOUT); // 超时 + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + }, userSetting.getPlatformPlayTimeout()); + // 添加监听 + int finalPort = port; + Boolean finalTcpActive = tcpActive; + + // 添加在本机上线的通知 + mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> { + dynamicTask.stop(callIdHeader.getCallId()); + if (serverId.equals(userSetting.getServerId())) { + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + return; + } + if (finalTcpActive != null) { + sendRtpItem.setTcpActive(finalTcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + } else { + // 其他平台内容 + otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + }); + + // 添加回复的拒绝或者错误的通知 + redisPushStreamResponseListener.addEvent(gbStream.getApp(), gbStream.getStream(), response -> { + if (response.getCode() != 0) { + dynamicTask.stop(callIdHeader.getCallId()); + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); + try { + responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 点播回复: {}", e.getMessage()); + } + } + }); + } + } + + /** + * 来自其他wvp的推流 + */ + private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + logger.info("[级联点播]直播流来自其他平台,发送redis消息"); + // 发送redis消息 + redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(), + streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId, + channelId, mediaTransmissionTCP, platform.isRtcp(),null, responseSendItemMsg -> { + SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem(); + if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + return; + } + // 收到sendItem + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request,sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + }, (wvpResult) -> { + + // 错误 + if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) { + // 离线 + // 查询是否在本机上线了 + StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream()); + if (currentStreamPushItem.isPushIng()) { + // 在线状态 + pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + + } else { + // 不在线 拉起 + notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + try { + responseAck(request, Response.BUSY_HERE); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 点播回复 BUSY_HERE: {}", e.getMessage()); + } + }); + } + + public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) { + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + sendRtpItem.getChannelId() + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("t=0 0\r\n"); + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口 + int localPort = sendRtpItem.getLocalPort(); + if(localPort == 0) + { + localPort = new Random().nextInt(65535) + 1; + } + content.append("m=video " + localPort + " RTP/AVP 96\r\n"); + content.append("a=sendonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + if (sendRtpItem.isTcp()) { + content.append("a=connection:new\r\n"); + if (!sendRtpItem.isTcpActive()) { + content.append("a=setup:active\r\n"); + } else { + content.append("a=setup:passive\r\n"); + } + } + content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); + content.append("f=\r\n"); + + try { + return responseSdpAck(request, content.toString(), platform); + } catch (SipException e) { + logger.error("未处理的异常 ", e); + } catch (InvalidArgumentException e) { + logger.error("未处理的异常 ", e); + } catch (ParseException e) { + logger.error("未处理的异常 ", e); + } + return null; + } + + public void inviteFromDeviceHandle(SIPRequest request, String requesterId) { + + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) + Device device = redisCatchStorage.getDevice(requesterId); + if (device != null) { + logger.info("收到设备" + requesterId + "的语音广播Invite请求"); + try { + responseAck(request, Response.TRYING); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + } + String contentString = new String(request.getRawContent()); + // jainSip不支持y=字段, 移除移除以解析。 + String substring = contentString; + String ssrc = "0000000404"; + int ssrcIndex = contentString.indexOf("y="); + if (ssrcIndex > 0) { + substring = contentString.substring(0, ssrcIndex); + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + } + ssrcIndex = substring.indexOf("f="); + if (ssrcIndex > 0) { + substring = contentString.substring(0, ssrcIndex); + } + SessionDescription sdp = null; + try { + sdp = SdpFactory.getInstance().createSessionDescription(substring); + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + int port = -1; + //boolean recvonly = false; + boolean mediaTransmissionTCP = false; + Boolean tcpActive = null; + for (int i = 0; i < mediaDescriptions.size(); i++) { + MediaDescription mediaDescription = (MediaDescription) mediaDescriptions.get(i); + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("8")) { + port = media.getMediaPort(); + String protocol = media.getProtocol(); + // 区分TCP发流还是udp, 当前默认udp + if ("TCP/RTP/AVP".equals(protocol)) { + String setup = mediaDescription.getAttribute("setup"); + if (setup != null) { + mediaTransmissionTCP = true; + if ("active".equals(setup)) { + tcpActive = true; + } else if ("passive".equals(setup)) { + tcpActive = false; + } + } + } + break; + } + } + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的媒体格式,返回415, {}", e.getMessage()); + } + return; + } + String username = sdp.getOrigin().getUsername(); + String addressStr = sdp.getConnection().getAddress(); + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); + } catch (SdpException e) { + logger.error("[SDP解析异常]", e); + } + + + + } else { + logger.warn("来自无效设备/平台的请求"); + try { + responseAck(request, Response.BAD_REQUEST);// 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage()); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java new file mode 100644 index 0000000..4189704 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java @@ -0,0 +1,281 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; +import javax.sip.header.FromHeader; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * SIP命令类型: NOTIFY请求中的目录请求处理 + */ +@Component +public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent { + + + private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForCatalogProcessor.class); + + private final List updateChannelOnlineList = new CopyOnWriteArrayList<>(); + private final List updateChannelOfflineList = new CopyOnWriteArrayList<>(); + private final Map updateChannelMap = new ConcurrentHashMap<>(); + + private final Map addChannelMap = new ConcurrentHashMap<>(); + private final List deleteChannelList = new CopyOnWriteArrayList<>(); + + + @Autowired + private UserSetting userSetting; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private DynamicTask dynamicTask; + + private final static String talkKey = "notify-request-for-catalog-task"; + + public void process(RequestEvent evt) { + try { + long start = System.currentTimeMillis(); + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null || device.getOnline() == 0) { + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); + return; + } + Element rootElement = getRootElement(evt, device.getCharset()); + if (rootElement == null) { + logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); + return; + } + Element deviceListElement = rootElement.element("DeviceList"); + if (deviceListElement == null) { + return; + } + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + Element eventElement = itemDevice.element("Event"); + String event; + if (eventElement == null) { + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); + event = CatalogEvent.ADD; + }else { + event = eventElement.getText().toUpperCase(); + } + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event); + + channel.setDeviceId(device.getDeviceId()); + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); + switch (event) { + case CatalogEvent.ON: + // 上线 + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + updateChannelOnlineList.add(channel); + if (updateChannelOnlineList.size() > 300) { + executeSaveForOnline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), true); + } + + break; + case CatalogEvent.OFF : + // 离线 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + updateChannelOfflineList.add(channel); + if (updateChannelOfflineList.size() > 300) { + executeSaveForOffline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + } + }else { + logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.VLOST: + // 视频丢失 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + updateChannelOfflineList.add(channel); + if (updateChannelOfflineList.size() > 300) { + executeSaveForOffline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + } + }else { + logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.DEFECT: + // 故障 + logger.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + updateChannelOfflineList.add(channel); + if (updateChannelOfflineList.size() > 300) { + executeSaveForOffline(); + } + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), channel.getChannelId(), false); + } + }else { + logger.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.ADD: + // 增加 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + // 判断此通道是否存在 + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId()); + if (deviceChannel != null) { + channel.setId(deviceChannel.getId()); + updateChannelMap.put(channel.getChannelId(), channel); + if (updateChannelMap.keySet().size() > 300) { + executeSaveForUpdate(); + } + }else { + addChannelMap.put(channel.getChannelId(), channel); + if (addChannelMap.keySet().size() > 300) { + executeSaveForAdd(); + } + } + + break; + case CatalogEvent.DEL: + // 删除 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deleteChannelList.add(channel); + if (deleteChannelList.size() > 300) { + executeSaveForDelete(); + } + break; + case CatalogEvent.UPDATE: + // 更新 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + // 判断此通道是否存在 + DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId()); + if (deviceChannelForUpdate != null) { + channel.setId(deviceChannelForUpdate.getId()); + updateChannelMap.put(channel.getChannelId(), channel); + if (updateChannelMap.keySet().size() > 300) { + executeSaveForUpdate(); + } + }else { + addChannelMap.put(channel.getChannelId(), channel); + if (addChannelMap.keySet().size() > 300) { + executeSaveForAdd(); + } + } + break; + default: + logger.warn("[ NotifyCatalog ] event not found : {}", event ); + + } + // 转发变化信息 + eventPublisher.catalogEventPublish(null, channel, event); + + if (updateChannelMap.keySet().size() > 0 + || addChannelMap.keySet().size() > 0 + || updateChannelOnlineList.size() > 0 + || updateChannelOfflineList.size() > 0 + || deleteChannelList.size() > 0) { + + if (!dynamicTask.contains(talkKey)) { + dynamicTask.startDelay(talkKey, this::executeSave, 1000); + } + } + } + } + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + private void executeSave(){ + logger.info("定时存储数据"); + executeSaveForUpdate(); + executeSaveForDelete(); + executeSaveForOnline(); + executeSaveForOffline(); + dynamicTask.stop(talkKey); + } + + private void executeSaveForUpdate(){ + if (updateChannelMap.values().size() > 0) { + ArrayList deviceChannels = new ArrayList<>(updateChannelMap.values()); + updateChannelMap.clear(); + deviceChannelService.batchUpdateChannel(deviceChannels); + } + + } + + private void executeSaveForAdd(){ + if (addChannelMap.values().size() > 0) { + ArrayList deviceChannels = new ArrayList<>(addChannelMap.values()); + addChannelMap.clear(); + deviceChannelService.batchAddChannel(deviceChannels); + } + } + + private void executeSaveForDelete(){ + if (deleteChannelList.size() > 0) { + deviceChannelService.deleteChannels(deleteChannelList); + deleteChannelList.clear(); + } + } + + private void executeSaveForOnline(){ + if (updateChannelOnlineList.size() > 0) { + deviceChannelService.channelsOnline(updateChannelOnlineList); + updateChannelOnlineList.clear(); + } + } + + private void executeSaveForOffline(){ + if (updateChannelOfflineList.size() > 0) { + deviceChannelService.channelsOffline(updateChannelOfflineList); + updateChannelOfflineList.clear(); + } + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/NotifyRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/NotifyRequestProcessor.java new file mode 100644 index 0000000..d5ea103 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/NotifyRequestProcessor.java @@ -0,0 +1,488 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.NumericUtil; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.redis.RedisUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * SIP命令类型: NOTIFY请求,这是作为上级发送订阅请求后,设备才会响应的 + */ +@Component +public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + + private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private EventPublisher publisher; + + private final String method = "NOTIFY"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private NotifyRequestForCatalogProcessor notifyRequestForCatalogProcessor; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + private int maxQueueCount = 30000; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + @Override + public void process(RequestEvent evt) { + try { + + if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) { + responseAck((SIPRequest) evt.getRequest(), Response.BUSY_HERE, null, null); + logger.error("[notify] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); + return; + }else { + responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null); + } + + }catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("未处理的异常 ", e); + } + boolean runed = !taskQueue.isEmpty(); + logger.info("[notify] 待处理消息数量: {}", taskQueue.size()); + taskQueue.offer(new HandlerCatchData(evt, null, null)); + if (!runed) { + taskExecutor.execute(()-> { + while (!taskQueue.isEmpty()) { + try { + HandlerCatchData take = taskQueue.poll(); + if (take == null) { + continue; + } + Element rootElement = getRootElement(take.getEvt()); + if (rootElement == null) { + logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest()); + continue; + } + String cmd = XmlUtil.getText(rootElement, "CmdType"); + + if (CmdType.CATALOG.equals(cmd)) { + logger.info("接收到Catalog通知"); +// processNotifyCatalogList(take.getEvt()); + notifyRequestForCatalogProcessor.process(take.getEvt()); + } else if (CmdType.ALARM.equals(cmd)) { + logger.info("接收到Alarm通知"); + processNotifyAlarm(take.getEvt()); + } else if (CmdType.MOBILE_POSITION.equals(cmd)) { + logger.info("接收到MobilePosition通知"); + processNotifyMobilePosition(take.getEvt()); + } else { + logger.info("接收到消息:" + cmd); + } + } catch (DocumentException e) { + logger.error("处理NOTIFY消息时错误", e); + } + } + }); + } + } + + /** + * 处理MobilePosition移动位置Notify + * + * @param evt + */ + private void processNotifyMobilePosition(RequestEvent evt) { + try { + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + + // 回复 200 OK + Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理MobilePosition移动位置Notify时未获取到消息体,{}", evt.getRequest()); + return; + } + + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); + + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getTextTrim(); + Device device = redisCatchStorage.getDevice(deviceId); + + if (device == null) { + device = redisCatchStorage.getDevice(channelId); + if (device == null) { + // 根据通道id查询设备Id + List deviceList = deviceChannelService.getDeviceByChannelId(channelId); + if (deviceList.size() > 0) { + device = deviceList.get(0); + } + } + } + if (device == null) { + logger.warn("[mobilePosition移动位置Notify] 未找到通道{}所属的设备", channelId); + return; + } + if (!ObjectUtils.isEmpty(device.getName())) { + mobilePosition.setDeviceName(device.getName()); + } + + mobilePosition.setDeviceId(device.getDeviceId()); + mobilePosition.setChannelId(channelId); + String time = XmlUtil.getText(rootElement, "Time"); + mobilePosition.setTime(time); + mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + logger.info("[收到移动位置订阅通知]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), + mobilePosition.getLongitude(), mobilePosition.getLatitude()); + mobilePosition.setReportSource("Mobile Position"); + + + // 更新device channel 的经纬度 + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(device.getDeviceId()); + deviceChannel.setChannelId(channelId); + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + + +// storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", time); + jsonObject.put("serial", deviceId); + jsonObject.put("code", channelId); + jsonObject.put("longitude", mobilePosition.getLongitude()); + jsonObject.put("latitude", mobilePosition.getLatitude()); + jsonObject.put("altitude", mobilePosition.getAltitude()); + jsonObject.put("direction", mobilePosition.getDirection()); + jsonObject.put("speed", mobilePosition.getSpeed()); + redisCatchStorage.sendMobilePositionMsg(jsonObject); + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + /*** + * 处理alarm设备报警Notify + * + * @param evt + */ + private void processNotifyAlarm(RequestEvent evt) { + if (!sipConfig.isAlarm()) { + return; + } + try { + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + + Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理alarm设备报警Notify时未获取到消息体{}", evt.getRequest()); + return; + } + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getText(); + + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null) { + logger.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId); + return; + } + rootElement = getRootElement(evt, device.getCharset()); + if (rootElement == null) { + logger.warn("[ NotifyAlarm ] content cannot be null, {}", evt.getRequest()); + return; + } + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setDeviceId(deviceId); + deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); + deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime"); + if (alarmTime == null) { + logger.warn("[ NotifyAlarm ] AlarmTime cannot be null"); + return; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); + if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription")); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) { + deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); + } else { + deviceAlarm.setLongitude(0.00); + } + if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) { + deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); + } else { + deviceAlarm.setLatitude(0.00); + } + logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId()); + if ("4".equals(deviceAlarm.getAlarmMethod())) { + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); + mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); + mobilePosition.setTime(deviceAlarm.getAlarmTime()); + mobilePosition.setLongitude(deviceAlarm.getLongitude()); + mobilePosition.setLatitude(deviceAlarm.getLatitude()); + mobilePosition.setReportSource("GPS Alarm"); + + // 更新device channel 的经纬度 + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(device.getDeviceId()); + deviceChannel.setChannelId(channelId); + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + +// storager.updateChannelPosition(deviceChannel); + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.getChannelId()); + jsonObject.put("longitude", mobilePosition.getLongitude()); + jsonObject.put("latitude", mobilePosition.getLatitude()); + jsonObject.put("altitude", mobilePosition.getAltitude()); + jsonObject.put("direction", mobilePosition.getDirection()); + jsonObject.put("speed", mobilePosition.getSpeed()); + redisCatchStorage.sendMobilePositionMsg(jsonObject); + + } + // TODO: 需要实现存储报警信息、报警分类 + + // 回复200 OK + if (redisCatchStorage.deviceIsOnline(deviceId)) { + publisher.deviceAlarmEventPublish(deviceAlarm); + + } + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + /*** + * 处理catalog设备目录列表Notify + * + * @param evt + */ + private void processNotifyCatalogList(RequestEvent evt) { + try { + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null || device.getOnline() == 0) { + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); + return; + } + Element rootElement = getRootElement(evt, device.getCharset()); + if (rootElement == null) { + logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); + return; + } + Element deviceListElement = rootElement.element("DeviceList"); + if (deviceListElement == null) { + return; + } + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + Element eventElement = itemDevice.element("Event"); + String event; + if (eventElement == null) { + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); + event = CatalogEvent.ADD; + }else { + event = eventElement.getText().toUpperCase(); + } + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event); + channel.setDeviceId(device.getDeviceId()); + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); + switch (event) { + case CatalogEvent.ON: + // 上线 + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + storager.deviceChannelOnline(deviceId, channel.getChannelId()); + break; + case CatalogEvent.OFF : + // 离线 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + storager.deviceChannelOffline(deviceId, channel.getChannelId()); + }else { + logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.VLOST: + // 视频丢失 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + if (userSetting.getRefuseChannelStatusChannelFormNotify()) { + storager.deviceChannelOffline(deviceId, channel.getChannelId()); + }else { + logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + } + break; + case CatalogEvent.DEFECT: + // 故障 + break; + case CatalogEvent.ADD: + // 增加 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); + break; + case CatalogEvent.DEL: + // 删除 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + storager.delChannel(deviceId, channel.getChannelId()); + break; + case CatalogEvent.UPDATE: + // 更新 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); + break; + default: + logger.warn("[ NotifyCatalog ] event not found : {}", event ); + + } + // 转发变化信息 + eventPublisher.catalogEventPublish(null, channel, event); + + } + } + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + public void setCmder(SIPCommander cmder) { + } + + public void setStorager(IVideoManagerStorage storager) { + this.storager = storager; + } + + public void setPublisher(EventPublisher publisher) { + this.publisher = publisher; + } + + public void setRedis(RedisUtil redis) { + } + + public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { + } + + public IRedisCatchStorage getRedisCatchStorage() { + return redisCatchStorage; + } + + public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) { + this.redisCatchStorage = redisCatchStorage; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RTPStreamer.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RTPStreamer.java new file mode 100644 index 0000000..c39c9bc --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RTPStreamer.java @@ -0,0 +1,135 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +public class RTPStreamer { + + // 配置参数 + private static final int RTP_PAYLOAD_TYPE = 100; // PT字段,动态类型96 + private static final int RTP_HEADER_SIZE = 12; // RTP头部固定大小 + private static final int MTU = 1000; // 最大传输单元 + private static final int SAMPLE_RATE = 90000; // 时间戳的采样率 + private static final int FRAME_RATE = 30; // 视频帧率 + private static final long TIMESTAMP_INCREMENT = SAMPLE_RATE / FRAME_RATE; // 时间戳增量 + + public static void sendRtp(String ffmpegCommand){ + // FFmpeg 命令字符串 +// String ffmpegCommand = "ffmpeg -i rtsp://source_ip:source_port/path -an -c:v libx264 -f mpegts udp://destination_ip:destination_port?pkt_size=1316"; + + // 使用 ProcessBuilder 来运行命令 + ProcessBuilder processBuilder = new ProcessBuilder(); + // 将命令分割成字符串数组并设置给 ProcessBuilder + processBuilder.command("bash", "-c", ffmpegCommand); + + try { + // 启动进程 + Process process = processBuilder.start(); + + // 获取进程的输入流(标准输出和标准错误) + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + // 打印 FFmpeg 的输出 + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + + // 打印 FFmpeg 的错误信息(如果有) + while ((line = errorReader.readLine()) != null) { + System.err.println(line); + } + + // 等待进程结束并获取退出码 + int exitCode = process.waitFor(); + System.out.println("FFmpeg process exited with code: " + exitCode); + + } catch (Exception e) { + e.printStackTrace(); + } + } + public static void streamVideo(String videoPath, String destinationIp, int destinationPort) throws Exception { + // 初始化RTP传输参数 + DatagramSocket socket = new DatagramSocket(); + InetAddress address = InetAddress.getByName(destinationIp); + int sequenceNumber = 0; + long timestamp = 0; + int ssrc = (int) (Math.random() * Integer.MAX_VALUE); + + // 构造FFmpeg命令 + String ffmpegCommand = String.format("ffmpeg -i %s -an -vcodec libx264 -bsf:v h264_mp4toannexb -f h264 -", videoPath); + + // 启动FFmpeg进程 + Process process = Runtime.getRuntime().exec(ffmpegCommand); + InputStream inputStream = process.getInputStream(); + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + // 读取FFmpeg输出(H.264编码数据) + byte[] buffer = new byte[MTU]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + // 将读取的 H.264 数据封装成 RTP 包 + byte[] rtpPacket = createRTPPacket(buffer, bytesRead, sequenceNumber++, timestamp, ssrc); + timestamp += TIMESTAMP_INCREMENT; + + // 发送 RTP 包 + DatagramPacket packet = new DatagramPacket(rtpPacket, rtpPacket.length, address, destinationPort); + socket.send(packet); + + // 打印日志 +// System.out.printf("Sent RTP packet, sequence: %d, timestamp: %d, size: %d%n", sequenceNumber, timestamp, bytesRead); + } + + // 关闭资源 + socket.close(); + process.destroy(); + + // 打印 FFmpeg 错误流(如果有) + String line; + while ((line = errorReader.readLine()) != null) { + System.err.println(line); + } + } + + // 构造 RTP 包 + private static byte[] createRTPPacket(byte[] payload, int payloadSize, int sequenceNumber, long timestamp, int ssrc) { + // RTP头部固定为12字节 + byte[] rtpPacket = new byte[RTP_HEADER_SIZE + payloadSize]; + rtpPacket[0] = (byte) (2 << 6); // Version = 2 + rtpPacket[1] = (byte) RTP_PAYLOAD_TYPE; // PT字段 + rtpPacket[2] = (byte) (sequenceNumber >> 8); // 序列号高字节 + rtpPacket[3] = (byte) (sequenceNumber & 0xFF); // 序列号低字节 + rtpPacket[4] = (byte) (timestamp >> 24); // 时间戳高字节 + rtpPacket[5] = (byte) (timestamp >> 16); + rtpPacket[6] = (byte) (timestamp >> 8); + rtpPacket[7] = (byte) (timestamp & 0xFF); // 时间戳低字节 + rtpPacket[8] = (byte) (ssrc >> 24); // SSRC高字节 + rtpPacket[9] = (byte) (ssrc >> 16); + rtpPacket[10] = (byte) (ssrc >> 8); + rtpPacket[11] = (byte) (ssrc & 0xFF); // SSRC低字节 + + // 复制负载数据 + System.arraycopy(payload, 0, rtpPacket, RTP_HEADER_SIZE, payloadSize); + + return rtpPacket; + } + + public static void main(String[] args) { + try { + // 输入 MP4 文件路径、目标 IP 和端口 + String videoPath = "/data-space/files/riis500/encoded_h264.h264"; + String destinationIp = "192.168.22.237"; + int destinationPort = 5083; + + // 启动视频流 + streamVideo(videoPath, destinationIp, destinationPort); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RTPVideoSender.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RTPVideoSender.java new file mode 100644 index 0000000..d73b1ff --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RTPVideoSender.java @@ -0,0 +1,111 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.nio.ByteBuffer; + +public class RTPVideoSender { + + private DatagramSocket socket; + private int localPort; + private String remoteIp; + private int remotePort; + private int payloadType; // 在Java中,这通常用于内部处理,不直接发送到网络 + private long ssrc; // 同步源标识符,在Java中作为long处理 + int MTU = 1200; // 最大传输单元 + + public RTPVideoSender(int localPort, String remoteIp, int remotePort, int payloadType, long ssrc) { + this.localPort = localPort; + this.remoteIp = remoteIp; + this.remotePort = remotePort; + this.payloadType = payloadType; // 注意:这个值在Java中可能不直接用于UDP数据包 + this.ssrc = ssrc; + + try { + this.socket = new DatagramSocket(localPort); + } catch (Exception e) { + e.printStackTrace(); + // 处理异常,例如通过抛出异常或记录日志 + } + } + + public boolean setup(byte[] payload) { + try { + // 这里没有直接设置SSRC或payloadType到UDP数据包,因为UDP是无连接的 + // 但你可以在你的应用层协议中包含这些信息 + + InetAddress address = InetAddress.getByName(remoteIp); + + // 假设这里有一个方法用于构建包含payloadType和ssrc的RTP数据包 + byte[] rtpPacket = buildRTPPacket(payloadType, ssrc, payload); + + // 发送数据包 + DatagramPacket packet = new DatagramPacket(rtpPacket, rtpPacket.length, address, remotePort); + socket.send(packet); + + System.out.println("RTP packet sent to " + remoteIp + ":" + remotePort); + + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + // 这是一个示例方法,用于构建RTP数据包(实际上你需要根据RTP规范来实现) + private byte[] buildRTPPacket(int payloadType, long ssrc, byte[] payload) { + // 这里应该包含实际的RTP头部构建逻辑 + // 但为了简单起见,我们仅返回一个模拟的字节数组 + // 注意:这只是一个示例,实际实现将更复杂 + byte[] header = new byte[12]; // RTP头部通常至少12字节长 + ByteBuffer buffer = ByteBuffer.wrap(header); + buffer.put((byte) ((2 << 6) & 0xC0)); // 版本号 + buffer.put((byte) 0x00); // 填充和扩展位 + int marker = 0; // 标记位,根据是否是最后一个包来设置 + + buffer.put((byte) ((marker << 7) | (payloadType & 0x7F))); // 标记和负载类型 + buffer.putShort((short) 0); // 序列号 + buffer.putInt(0); // 时间戳 + buffer.putInt((int) (Math.random() * Integer.MAX_VALUE)); // SSRC + // 填充头部(这里省略了实际的头部构建逻辑) + + byte[] fullPacket = new byte[header.length + payload.length]; + System.arraycopy(header, 0, fullPacket, 0, header.length); + System.arraycopy(payload, 0, fullPacket, header.length, payload.length); + + return fullPacket; + } + + // 关闭套接字 + public void close() { + if (socket != null) { + socket.close(); + } + } + + + public static void main(String[] args) throws Exception { + RTPVideoSender rtpVideoSender = new RTPVideoSender(5083, "192.168.22.237", 5083,100, 20002343); + // 构造FFmpeg命令 + String ffmpegCommand = String.format("ffmpeg -i %s -an -vcodec libx264 -f h264 -", "D:\\ffmpeg\\bin\\input.mp4"); + + // 启动FFmpeg进程 + Process process = Runtime.getRuntime().exec(ffmpegCommand); + InputStream inputStream = process.getInputStream(); + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + // 读取FFmpeg输出(H.264编码数据) + byte[] buffer = new byte[1000]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + // 将读取的 H.264 数据封装成 RTP 包 + rtpVideoSender.setup(buffer); + } + process.destroy(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RegisterRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RegisterRequestProcessor.java new file mode 100644 index 0000000..c3a119e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/RegisterRequestProcessor.java @@ -0,0 +1,223 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.auth.DigestServerAuthenticationHelper; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.RemoteAddressInfo; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.bean.WvpSipDate; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.utils.DateUtil; +import gov.nist.javax.sip.RequestEventExt; +import gov.nist.javax.sip.address.AddressImpl; +import gov.nist.javax.sip.address.SipUri; +import gov.nist.javax.sip.header.SIPDateHeader; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.AuthorizationHeader; +import javax.sip.header.ContactHeader; +import javax.sip.header.FromHeader; +import javax.sip.header.ViaHeader; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Locale; + +/** + * SIP命令类型: REGISTER请求 + */ +@Component +public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); + + public final String method = "REGISTER"; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private SIPSender sipSender; + + @Autowired + private UserSetting userSetting; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 收到注册请求 处理 + * + * @param evt + */ + @Override + public void process(RequestEvent evt) { + try { + RequestEventExt evtExt = (RequestEventExt) evt; + String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); + + SIPRequest request = (SIPRequest)evt.getRequest(); + Response response = null; + boolean passwordCorrect = false; + // 注册标志 + boolean registerFlag; + FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); + AddressImpl address = (AddressImpl) fromHeader.getAddress(); + SipUri uri = (SipUri) address.getURI(); + String deviceId = uri.getUser(); + logger.info("[注册请求] 设备:{}, 开始处理: {}", deviceId, requestAddress); + Device device = deviceService.getDevice(deviceId); + + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, + userSetting.getSipUseSourceIpAsRemoteAddress()); + + if (device != null && + device.getSipTransactionInfo() != null && + request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) { + logger.info("[注册请求] 设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId()); + device.setExpires(request.getExpires().getExpires()); + device.setIp(remoteAddressInfo.getIp()); + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setLocalIp(request.getLocalAddress().getHostAddress()); + Response registerOkResponse = getRegisterOkResponse(request); + // 判断TCP还是UDP + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); + String transport = reqViaHeader.getTransport(); + device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP"); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse); + device.setRegisterTime(DateUtil.getNow()); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse); + deviceService.online(device, sipTransactionInfo); + return; + } + String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword(); + AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); + if (authHead == null && !ObjectUtils.isEmpty(password)) { + logger.info("[注册请求] 设备:{}, 回复401: {}",deviceId, requestAddress); + response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); + new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } + + // 校验密码是否正确 + passwordCorrect = ObjectUtils.isEmpty(password) || + new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password); + + if (!passwordCorrect) { + // 注册失败 + response = getMessageFactory().createResponse(Response.FORBIDDEN, request); + response.setReasonPhrase("wrong password"); + logger.info("[注册请求] 设备:{}, 密码/SIP服务器ID错误, 回复403: {}", deviceId, requestAddress); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } + + // 携带授权头并且密码正确 + response = getMessageFactory().createResponse(Response.OK, request); + // 添加date头 + SIPDateHeader dateHeader = new SIPDateHeader(); + // 使用自己修改的 + WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis()); + dateHeader.setDate(wvpSipDate); + response.addHeader(dateHeader); + + if (request.getExpires() == null) { + response = getMessageFactory().createResponse(Response.BAD_REQUEST, request); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } + // 添加Contact头 + response.addHeader(request.getHeader(ContactHeader.NAME)); + // 添加Expires头 + response.addHeader(request.getExpires()); + + if (device == null) { + device = new Device(); + device.setStreamMode("UDP"); + device.setCharset("GB2312"); + device.setGeoCoordSys("WGS84"); + device.setTreeType("CivilCode"); + device.setDeviceId(deviceId); + device.setOnline(0); + } + device.setIp(remoteAddressInfo.getIp()); + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setLocalIp(request.getLocalAddress().getHostAddress()); + if (request.getExpires().getExpires() == 0) { + // 注销成功 + registerFlag = false; + } else { + // 注册成功 + device.setExpires(request.getExpires().getExpires()); + registerFlag = true; + // 判断TCP还是UDP + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); + String transport = reqViaHeader.getTransport(); + device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP"); + } + + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + // 注册成功 + // 保存到redis + if (registerFlag) { + logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); + device.setRegisterTime(DateUtil.getNow()); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response); + deviceService.online(device, sipTransactionInfo); + } else { + logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); + deviceService.offline(deviceId, "主动注销"); + } + } catch (SipException | NoSuchAlgorithmException | ParseException e) { + logger.error("未处理的异常 ", e); + } + } + + private Response getRegisterOkResponse(Request request) throws ParseException { + // 携带授权头并且密码正确 + Response response = getMessageFactory().createResponse(Response.OK, request); + // 添加date头 + SIPDateHeader dateHeader = new SIPDateHeader(); + // 使用自己修改的 + WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis()); + dateHeader.setDate(wvpSipDate); + response.addHeader(dateHeader); + + // 添加Contact头 + response.addHeader(request.getHeader(ContactHeader.NAME)); + // 添加Expires头 + response.addHeader(request.getExpires()); + + return response; + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/SubscribeRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/SubscribeRequestProcessor.java new file mode 100644 index 0000000..ab57109 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/SubscribeRequestProcessor.java @@ -0,0 +1,212 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.monitor.gdw2019.bean.CmdType; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SubscribeHolder; +import com.yfd.monitor.gdw2019.bean.SubscribeInfo; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.w3c.dom.NodeList; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.ExpiresHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.Iterator; + +/** + * SIP命令类型: SUBSCRIBE请求 + */ +@Component +public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class); + private final String method = "SUBSCRIBE"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private SIPSender sipSender; + + @Autowired + ISIPCommanderForPlatform commanderForPlatform; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + /** + * 处理SUBSCRIBE请求 + * + * @param evt 事件 + */ + @Override + public void process(RequestEvent evt) { + SIPRequest request = (SIPRequest) evt.getRequest(); + try { + Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理SUBSCRIBE请求 未获取到消息体{}", evt.getRequest()); + return; + } + String name = rootElement.getName(); + if("SIP_XML".equals(name)){//来自上级系统 + String cmd = rootElement.attributeValue("EventType"); + if ("Subscribe_Alarm".equals(cmd)) { + processNotifyAlarm(request, rootElement); + } else if ("Subscribe_Status".equals(cmd)) { + processNotifyStatus(request, rootElement); + } + + } + + } catch (SipException | DocumentException e ) { + logger.error("未处理的异常 ", e); + } + + } + + + + public void processNotifyAlarm(SIPRequest request, Element rootElement)throws SipException { + if (request == null) { + return; + } + String platformId = SipUtils.getUserIdFromFromHeader(request); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null){ + return; + } + String EventType = rootElement.attributeValue("EventType"); + SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); + subscribeInfo.setEventType(EventType); + Iterator itemList = rootElement.elementIterator("Item"); + JSONArray jsonArray=new JSONArray(); + while (itemList.hasNext()) { + Element itemElement = itemList.next(); + JSONObject job=new JSONObject(); + job.putOpt("code",itemElement.attributeValue("Code")); + job.putOpt("type",itemElement.attributeValue("Type")); + jsonArray.add(job); + } + subscribeInfo.setItems(JSONUtil.toJsonStr(jsonArray)); + try { + SIPResponse response = responseSubscribeAck(request, 200,subscribeInfo.getExpires()); + if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeAlarmSubscribe(platformId); + }else { + subscribeInfo.setResponse(response); + subscribeHolder.putAlarmSubscribe(platformId, subscribeInfo); + commanderForPlatform.sendNotify(platform,subscribeInfo,null,null);//向上级发送订阅成功的通知 + } + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("未处理的异常 ", e); + } + } + + private void processNotifyStatus(SIPRequest request, Element rootElement)throws SipException { + if (request == null) { + return; + } + String platformId = SipUtils.getUserIdFromFromHeader(request); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null){ + return; + } + String EventType = rootElement.attributeValue("EventType"); + SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); + subscribeInfo.setEventType(EventType); + Iterator itemList = rootElement.elementIterator("Item"); + JSONArray jsonArray=new JSONArray(); + while (itemList.hasNext()) { + Element itemElement = itemList.next(); + JSONObject job=new JSONObject(); + job.putOpt("code",itemElement.attributeValue("Code")); + jsonArray.add(job); + } + subscribeInfo.setItems(JSONUtil.toJsonStr(jsonArray)); + try { + SIPResponse response = responseSubscribeAck(request, 200,subscribeInfo.getExpires()); + if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeAlarmSubscribe(platformId); + }else { + subscribeInfo.setResponse(response); + subscribeHolder.putAlarmSubscribe(platformId, subscribeInfo); + commanderForPlatform.sendNotify(platform,subscribeInfo,null,null);//向上级发送订阅成功的通知 + } + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("未处理的异常 ", e); + } + } + + private void processNotifyCatalogList(SIPRequest request, Element rootElement) throws SipException { + if (request == null) { + return; + } + String platformId = SipUtils.getUserIdFromFromHeader(request); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null){ + return; + } + SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); + + String sn = XmlUtil.getText(rootElement, "SN"); + logger.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId); + StringBuilder resultXml = new StringBuilder(200); + resultXml.append("\r\n") + .append("\r\n") + .append("Catalog\r\n") + .append("").append(sn).append("\r\n") + .append("").append(deviceId).append("\r\n") + .append("OK\r\n") + .append("\r\n"); + + if (subscribeInfo.getExpires() > 0) { + subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo); + }else if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeCatalogSubscribe(platformId); + } + try { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId); + SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires()); + if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeCatalogSubscribe(platformId); + }else { + subscribeInfo.setResponse(response); + subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo); + } + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("未处理的异常 ", e); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/info/InfoRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/info/InfoRequestProcessor.java new file mode 100644 index 0000000..33b5d62 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/info/InfoRequestProcessor.java @@ -0,0 +1,139 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.info; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.*; +import javax.sip.message.Response; +import java.text.ParseException; + +@Component +public class InfoRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final static Logger logger = LoggerFactory.getLogger(InfoRequestProcessor.class); + + private final String method = "INFO"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IVideoManagerStorage storage; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + @Override + public void process(RequestEvent evt) { + logger.debug("接收到消息:" + evt.getRequest()); + SIPRequest request = (SIPRequest) evt.getRequest(); + String deviceId = SipUtils.getUserIdFromFromHeader(request); + CallIdHeader callIdHeader = request.getCallIdHeader(); + // 先从会话内查找 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + + // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + if (ssrcTransaction != null) { + deviceId = ssrcTransaction.getDeviceId(); + } + // 查询设备是否存在 + Device device = redisCatchStorage.getDevice(deviceId); + // 查询上级平台是否存在 + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId); + try { + if (device != null && parentPlatform != null) { + logger.warn("[重复]平台与设备编号重复:{}", deviceId); + String hostAddress = request.getRemoteAddress().getHostAddress(); + int remotePort = request.getRemotePort(); + if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) { + parentPlatform = null; + }else { + device = null; + } + } + if (device == null && parentPlatform == null) { + // 不存在则回复404 + responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found"); + logger.warn("[设备未找到 ]: {}", deviceId); + if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){ + DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog()); + deviceNotFoundEvent.setCallId(callIdHeader.getCallId()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent); + sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult); + } + }else { + ContentTypeHeader header = (ContentTypeHeader)evt.getRequest().getHeader(ContentTypeHeader.NAME); + String contentType = header.getContentType(); + String contentSubType = header.getContentSubType(); + if ("Application".equalsIgnoreCase(contentType) && "MANSRTSP".equalsIgnoreCase(contentSubType)) { + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, null, null, callIdHeader.getCallId()); + String streamId = sendRtpItem.getStreamId(); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + responseAck(request, Response.NOT_FOUND, "stream " + streamId + " not found"); + return; + } + Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID()); + cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> { + // 失败的回复 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); + } + }, eventResult -> { + // 成功的回复 + try { + responseAck(request, eventResult.statusCode); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage()); + } + }); + } + } + } catch (SipException e) { + logger.warn("SIP 回复错误", e); + } catch (InvalidArgumentException e) { + logger.warn("参数无效", e); + } catch (ParseException e) { + logger.warn("SIP回复时解析异常", e); + } + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/IMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/IMessageHandler.java new file mode 100644 index 0000000..4ad60c9 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/IMessageHandler.java @@ -0,0 +1,27 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import org.dom4j.Element; + +import javax.sdp.SdpParseException; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import java.text.ParseException; + +public interface IMessageHandler { + /** + * 处理来自设备的信息 + * @param evt + * @param device + */ + void handForDevice(RequestEvent evt, Device device, Element element); + + /** + * 处理来自平台的信息 + * @param evt + * @param parentPlatform + */ + void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) throws InvalidArgumentException, ParseException, SipException, SdpParseException; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/MessageHandlerAbstract.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/MessageHandlerAbstract.java new file mode 100644 index 0000000..b980a1a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/MessageHandlerAbstract.java @@ -0,0 +1,50 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message; + +import cn.hutool.core.util.ObjUtil; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import org.dom4j.Element; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.sdp.SdpParseException; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{ + + public Map messageHandlerMap = new ConcurrentHashMap<>(); + + public void addHandler(String cmdType, IMessageHandler messageHandler) { + messageHandlerMap.put(cmdType, messageHandler); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + String cmd = getText(element, "CmdType"); + IMessageHandler messageHandler = messageHandlerMap.get(cmd); + if (messageHandler != null) { + messageHandler.handForDevice(evt, device, element); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) throws ParseException, SipException, InvalidArgumentException { + String cmd = element.attributeValue("EventType"); + IMessageHandler messageHandler = messageHandlerMap.get(cmd); + if (messageHandler != null) { + try { + messageHandler.handForPlatform(evt, parentPlatform, element); + } catch (SdpParseException e) { + e.printStackTrace(); + } + } + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/MessageRequestProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/MessageRequestProcessor.java new file mode 100644 index 0000000..9f5b8f6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/MessageRequestProcessor.java @@ -0,0 +1,173 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.event.request.ISIPRequestProcessor; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.DeviceMapper; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sdp.SdpParseException; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class MessageRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { + + private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class); + + private final String method = "MESSAGE"; + + private static Map messageHandlerMap = new ConcurrentHashMap<>(); + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private IVideoManagerStorage storage; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private VideoStreamSessionManager sessionManager; + @Autowired + private DeviceChannelMapper deviceChannelMapper; + @Autowired + private DeviceMapper deviceMapper; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + public void addHandler(String name, IMessageHandler handler) { + messageHandlerMap.put(name, handler); + } + + @Override + public void process(RequestEvent evt) { + SIPRequest sipRequest = (SIPRequest) evt.getRequest(); + String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); + CallIdHeader callIdHeader = sipRequest.getCallIdHeader(); + // 先从会话内查找 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + if (ssrcTransaction != null) { + deviceId = ssrcTransaction.getDeviceId(); + } + SIPRequest request = (SIPRequest) evt.getRequest(); + String requesterId = SipUtils.getUserIdFromFromHeader(request); + String deviceid = deviceId; + if (deviceId.length() == 18) { + List channels = deviceChannelMapper.queryChannelList(deviceId); + if (channels.size() > 0) { + deviceid = channels.get(0).getDeviceId(); + } + } + // 查询设备是否存在 +// Device device = redisCatchStorage.getDevice(deviceid); + Device device = deviceMapper.getDeviceByDeviceId(deviceid); + if (device == null) { + logger.error("===================================设备不存在=================================="+sipRequest.toString()); + } + // 查询上级平台是否存在 + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(requesterId); + try { + if (device != null && parentPlatform != null) { + String hostAddress = request.getRemoteAddress().getHostAddress(); + int remotePort = request.getRemotePort(); + if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) { + parentPlatform = null; + } else { + device = null; + } + } + if (device == null && parentPlatform == null) { + // 不存在则回复404 + responseAck(request, Response.NOT_FOUND, "device " + deviceId + " not found"); + logger.error("[设备未找到 ]deviceId: {}, callId: {}", deviceId, callIdHeader.getCallId()); + if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null) { + DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog()); + deviceNotFoundEvent.setCallId(callIdHeader.getCallId()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(deviceNotFoundEvent); + sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()).response(eventResult); + } + } else { + Element rootElement = null; + try { + rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理MESSAGE请求 未获取到消息体{}", evt.getRequest()); + responseAck(request, Response.BAD_REQUEST, "content is null"); + return; + } + } catch (DocumentException e) { + logger.warn("解析XML消息内容异常", e); + // 不存在则回复404 + responseAck(request, Response.BAD_REQUEST, e.getMessage()); + } + String name = rootElement.getName(); + if(!"Notify".equals(name)){ +// logger.error("接收到消息:" + evt.getRequest()); + } + if ("SIP_XML".equals(name)) { //来自上级系统 +// logger.error("========================================来自上级系统"); + IMessageHandler messageHandler = messageHandlerMap.get(name); + if (messageHandler != null) { + messageHandler.handForPlatform(evt, parentPlatform, rootElement); + } else { + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Request_Resource///"); + } + + } else {//来自设备请求 + IMessageHandler messageHandler = messageHandlerMap.get(name); + if (messageHandler != null) { + if (device != null) { + messageHandler.handForDevice(evt, device, rootElement); + } else { // 由于上面已经判断都为null则直接返回,所以这里device和parentPlatform必有一个不为null + messageHandler.handForPlatform(evt, parentPlatform, rootElement); + } + } else { + // 不支持的message + // 不存在则回复415 + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response"); + } + } + + } + } catch (SipException e) { + logger.warn("SIP 回复错误", e); + } catch (InvalidArgumentException e) { + logger.warn("参数无效", e); + } catch (ParseException | SdpParseException e) { + logger.warn("SIP回复时解析异常", e); + } + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/control/ControlMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/control/ControlMessageHandler.java new file mode 100644 index 0000000..70cceb6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/control/ControlMessageHandler.java @@ -0,0 +1,27 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.control; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 控制命令 + * 命令类型: 设备控制: 远程启动, 录像控制(TODO), 报警布防/撤防命令(TODO), 报警复位命令(TODO), + * 强制关键帧命令(TODO), 拉框放大/缩小控制命令(TODO), 看守位控制(TODO), 报警复位(TODO) + * 命令类型: 设备配置: SVAC编码配置(TODO), 音频参数(TODO), SVAC解码配置(TODO) + */ +@Component +public class ControlMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Control"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java new file mode 100644 index 0000000..829c989 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java @@ -0,0 +1,358 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.control.cmd; + +import com.yfd.monitor.common.enums.DeviceControlType; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DragZoomRequest; +import com.yfd.monitor.gdw2019.bean.HomePositionRequest; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.control.ControlMessageHandler; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.*; +import javax.sip.address.SipURI; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.List; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.*; + +@Component +public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceControlQueryMessageHandler.class); + private final String cmdType = "DeviceControl"; + + @Autowired + private ControlMessageHandler controlMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void afterPropertiesSet() throws Exception { + controlMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + SIPRequest request = (SIPRequest) evt.getRequest(); + + // 此处是上级发出的DeviceControl指令 + String targetGBId = ((SipURI) request.getToHeader().getAddress().getURI()).getUser(); + String channelId = getText(rootElement, "DeviceID"); + // 远程启动功能 + if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) { + // TODO 拒绝远程启动命令 + logger.warn("[国标级联]收到平台的远程启动命令, 不处理"); + +// if (parentPlatform.getServerGBId().equals(targetGBId)) { +// // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 +// logger.info("执行远程启动本平台命令"); +// try { +// cmderFroPlatform.unregister(parentPlatform, null, null); +// } catch (InvalidArgumentException | ParseException | SipException e) { +// logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); +// } +// taskExecutor.execute(() -> { +// // 远程启动 +//// try { +//// Thread.sleep(3000); +//// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); +//// SipStackImpl stack = (SipStackImpl)up.getSipStack(); +//// stack.stop(); +//// Iterator listener = stack.getListeningPoints(); +//// while (listener.hasNext()) { +//// stack.deleteListeningPoint((ListeningPoint) listener.next()); +//// } +//// Iterator providers = stack.getSipProviders(); +//// while (providers.hasNext()) { +//// stack.deleteSipProvider((SipProvider) providers.next()); +//// } +//// VManageBootstrap.restart(); +//// } catch (InterruptedException | ObjectInUseException e) { +//// logger.error("[任务执行失败] 服务重启: {}", e.getMessage()); +//// } +// }); +// } + } + DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement); + logger.info("[接受deviceControl命令] 命令: {}", deviceControlType); + if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)) { + //判断是否存在该通道 + Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); + if (deviceForPlatform == null) { + try { + responseAck(request, Response.NOT_FOUND); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + switch (deviceControlType) { + case PTZ: + handlePtzCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.PTZ); + break; + case ALARM: + handleAlarmCmd(deviceForPlatform, rootElement, request); + break; + case GUARD: + handleGuardCmd(deviceForPlatform, rootElement, request, DeviceControlType.GUARD); + break; + case RECORD: + handleRecordCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.RECORD); + break; + case I_FRAME: + handleIFameCmd(deviceForPlatform, request, channelId); + break; + case TELE_BOOT: + handleTeleBootCmd(deviceForPlatform, request); + break; + case DRAG_ZOOM_IN: + handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_IN); + break; + case DRAG_ZOOM_OUT: + handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT); + break; + case HOME_POSITION: + handleHomePositionCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.HOME_POSITION); + break; + default: + break; + } + } + } + + /** + * 处理云台指令 + * + * @param device 设备 + * @param channelId 通道id + * @param rootElement + * @param request + */ + private void handlePtzCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + String cmdString = getText(rootElement, type.getVal()); + try { + cmder.fronEndCmd(device, channelId, cmdString, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); + } + } + + /** + * 处理强制关键帧 + * + * @param device 设备 + * @param channelId 通道id + */ + private void handleIFameCmd(Device device, SIPRequest request, String channelId) { + try { + cmder.iFrameCmd(device, channelId); + responseAck(request, Response.OK); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); + } + } + + /** + * 处理重启命令 + * + * @param device 设备信息 + */ + private void handleTeleBootCmd(Device device, SIPRequest request) { + try { + cmder.teleBootCmd(device); + responseAck(request, Response.OK); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 重启: {}", e.getMessage()); + } + + } + + /** + * 处理拉框控制*** + * + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param type 消息类型 + */ + private void handleDragZoom(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + try { + DragZoomRequest dragZoomRequest = loadElement(rootElement, DragZoomRequest.class); + DragZoomRequest.DragZoom dragZoom = dragZoomRequest.getDragZoomIn(); + if (dragZoom == null) { + dragZoom = dragZoomRequest.getDragZoomOut(); + } + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("<" + type.getVal() + ">\r\n"); + cmdXml.append("" + dragZoom.getLength() + "\r\n"); + cmdXml.append("" + dragZoom.getWidth() + "\r\n"); + cmdXml.append("" + dragZoom.getMidPointX() + "\r\n"); + cmdXml.append("" + dragZoom.getMidPointY() + "\r\n"); + cmdXml.append("" + dragZoom.getLengthX() + "\r\n"); + cmdXml.append("" + dragZoom.getLengthY() + "\r\n"); + cmdXml.append("\r\n"); + cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + responseAck(request, Response.OK); + } catch (Exception e) { + logger.error("[命令发送失败] 拉框控制: {}", e.getMessage()); + } + + } + + /** + * 处理看守位命令*** + * + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleHomePositionCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + try { + HomePositionRequest homePosition = loadElement(rootElement, HomePositionRequest.class); + //获取整个消息主体,我们只需要修改请求头即可 + HomePositionRequest.HomePosition info = homePosition.getHomePosition(); + cmder.homePositionCmd(device, channelId, info.getEnabled(), info.getResetTime(), info.getPresetIndex(), + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (Exception e) { + logger.error("[命令发送失败] 看守位设置: {}", e.getMessage()); + } + } + + /** + * 处理告警消息*** + * + * @param device 设备信息 + * @param rootElement 根节点 + * @param request 请求信息 + */ + private void handleAlarmCmd(Device device, Element rootElement, SIPRequest request) { + //告警方法 + String alarmMethod = ""; + //告警类型 + String alarmType = ""; + List info = rootElement.elements("Info"); + if (info != null) { + for (Element element : info) { + alarmMethod = getText(element, "AlarmMethod"); + alarmType = getText(element, "AlarmType"); + } + } + try { + cmder.alarmCmd(device, alarmMethod, alarmType, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 告警消息: {}", e.getMessage()); + } + } + + /** + * 处理录像控制 + * + * @param device 设备信息 + * @param channelId 通道id + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleRecordCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) { + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement, type.getVal()); + try { + cmder.recordCmd(device, channelId, cmdString, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 录像控制: {}", e.getMessage()); + } + } + + /** + * 处理报警布防/撤防命令 + * + * @param device 设备信息 + * @param rootElement 根节点 + * @param request 请求信息 + * @param type 消息类型 + */ + private void handleGuardCmd(Device device, Element rootElement, SIPRequest request, DeviceControlType type) { + //获取整个消息主体,我们只需要修改请求头即可 + String cmdString = getText(rootElement, type.getVal()); + try { + cmder.guardCmd(device, cmdString, + errorResult -> onError(request, errorResult), + okResult -> onOk(request, okResult)); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 布防/撤防命令: {}", e.getMessage()); + } + } + + + /** + * 错误响应处理 + * + * @param request 请求 + * @param eventResult 响应结构 + */ + private void onError(SIPRequest request, SipSubscribe.EventResult eventResult) { + // 失败的回复 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 回复: {}", e.getMessage()); + } + } + + /** + * 成功响应处理 + * + * @param request 请求 + * @param eventResult 响应结构 + */ + private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult) { + // 成功的回复 + try { + responseAck(request, eventResult.statusCode); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 回复: {}", e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/NotifyMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/NotifyMessageHandler.java new file mode 100644 index 0000000..25f08b6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/NotifyMessageHandler.java @@ -0,0 +1,26 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 通知命令, 参看 A.2.5 通知命令 + * 命令类型: 状态信息(心跳)报送, 报警通知, 媒体通知, 移动设备位置数据,语音广播通知(TODO), 设备预置位(TODO) +* + */ +@Component +public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Notify"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java new file mode 100644 index 0000000..d562167 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java @@ -0,0 +1,281 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.cmd; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.NotifyMessageHandler; +import com.yfd.monitor.gdw2019.utils.NumericUtil; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import com.yfd.monitor.service.IDeviceAlarmService; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.concurrent.ConcurrentLinkedQueue; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +/** + * 报警事件的处理,参考:9.4 + */ +@Component +public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private final Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); + private final String cmdType = "Alarm"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private EventPublisher publisher; + + @Autowired + private UserSetting userSetting; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceAlarmService deviceAlarmService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + logger.info("[收到报警通知]设备:{}", device.getDeviceId()); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 报警通知回复: {}", e.getMessage()); + } + if (isEmpty) { + taskExecutor.execute(() -> { + logger.info("[处理报警通知]待处理数量:{}", taskQueue.size() ); + while (!taskQueue.isEmpty()) { + try { + SipMsgInfo sipMsgInfo = taskQueue.poll(); + + Element deviceIdElement = sipMsgInfo.getRootElement().element("DeviceID"); + String channelId = deviceIdElement.getText(); + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); + deviceAlarm.setChannelId(channelId); + deviceAlarm.setAlarmPriority(getText(sipMsgInfo.getRootElement(), "AlarmPriority")); + deviceAlarm.setAlarmMethod(getText(sipMsgInfo.getRootElement(), "AlarmMethod")); + String alarmTime = XmlUtil.getText(sipMsgInfo.getRootElement(), "AlarmTime"); + if (alarmTime == null) { + continue; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); + String alarmDescription = getText(sipMsgInfo.getRootElement(), "AlarmDescription"); + if (alarmDescription == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(alarmDescription); + } + String longitude = getText(sipMsgInfo.getRootElement(), "Longitude"); + if (longitude != null && NumericUtil.isDouble(longitude)) { + deviceAlarm.setLongitude(Double.parseDouble(longitude)); + } else { + deviceAlarm.setLongitude(0.00); + } + String latitude = getText(sipMsgInfo.getRootElement(), "Latitude"); + if (latitude != null && NumericUtil.isDouble(latitude)) { + deviceAlarm.setLatitude(Double.parseDouble(latitude)); + } else { + deviceAlarm.setLatitude(0.00); + } + + if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod())) { + if ( deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.GPS.getVal() + "")) { + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); + mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); + mobilePosition.setTime(deviceAlarm.getAlarmTime()); + mobilePosition.setLongitude(deviceAlarm.getLongitude()); + mobilePosition.setLatitude(deviceAlarm.getLatitude()); + mobilePosition.setReportSource("GPS Alarm"); + + // 更新device channel 的经纬度 + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); + deviceChannel.setChannelId(channelId); + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, sipMsgInfo.getDevice()); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + +// storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.getChannelId()); + jsonObject.put("longitude", mobilePosition.getLongitude()); + jsonObject.put("latitude", mobilePosition.getLatitude()); + jsonObject.put("altitude", mobilePosition.getAltitude()); + jsonObject.put("direction", mobilePosition.getDirection()); + jsonObject.put("speed", mobilePosition.getSpeed()); + redisCatchStorage.sendMobilePositionMsg(jsonObject); + } + } + if (!ObjectUtils.isEmpty(deviceAlarm.getDeviceId())) { + if (deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.Video.getVal() + "")) { + deviceAlarm.setAlarmType(getText(sipMsgInfo.getRootElement().element("Info"), "AlarmType")); + } + } + logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm)); + // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里 + if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) { + // 发送给平台的报警信息。 发送redis通知 + logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm)); + AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); + if (deviceAlarm.getAlarmMethod() != null) { + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + } + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + if (deviceAlarm.getAlarmType() != null) { + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType())); + } + alarmChannelMessage.setGbId(channelId); + redisCatchStorage.sendAlarmMsg(alarmChannelMessage); + continue; + } + + logger.debug("存储报警信息、报警分类"); + // 存储报警信息、报警分类 + if (sipConfig.isAlarm()) { + deviceAlarmService.add(deviceAlarm); + } + + if (redisCatchStorage.deviceIsOnline(sipMsgInfo.getDevice().getDeviceId())) { + publisher.deviceAlarmEventPublish(deviceAlarm); + } + }catch (Exception e) { + logger.error("未处理的异常 ", e); + logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + } + } + }); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + logger.info("收到来自平台[{}]的报警通知", parentPlatform.getServerGBId()); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 报警通知回复: {}", e.getMessage()); + } + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getText(); + + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setDeviceId(parentPlatform.getServerGBId()); + deviceAlarm.setChannelId(channelId); + deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority")); + deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod")); + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime"); + if (alarmTime == null) { + return; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); + String alarmDescription = getText(rootElement, "AlarmDescription"); + if (alarmDescription == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(alarmDescription); + } + String longitude = getText(rootElement, "Longitude"); + if (longitude != null && NumericUtil.isDouble(longitude)) { + deviceAlarm.setLongitude(Double.parseDouble(longitude)); + } else { + deviceAlarm.setLongitude(0.00); + } + String latitude = getText(rootElement, "Latitude"); + if (latitude != null && NumericUtil.isDouble(latitude)) { + deviceAlarm.setLatitude(Double.parseDouble(latitude)); + } else { + deviceAlarm.setLatitude(0.00); + } + + if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod())) { + + if (deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.Video.getVal() + "")) { + deviceAlarm.setAlarmType(getText(rootElement.element("Info"), "AlarmType")); + } + } + + if (channelId.equals(parentPlatform.getDeviceGBId())) { + // 发送给平台的报警信息。 发送redis通知 + AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); + if (deviceAlarm.getAlarmMethod() != null) { + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + } + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + alarmChannelMessage.setGbId(channelId); + if (deviceAlarm.getAlarmType() != null) { + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType())); + } + redisCatchStorage.sendAlarmMsg(alarmChannelMessage); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java new file mode 100644 index 0000000..a7da79f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -0,0 +1,113 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.cmd; + +import cn.hutool.core.util.StrUtil; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.RemoteAddressInfo; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.NotifyMessageHandler; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +/** + * 状态信息(心跳)报送 + */ +@Component +public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + + private Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class); + private final static String cmdType = "Keepalive"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + if (device == null) { + // 未注册的设备不做处理 + return; + } + SIPRequest request = (SIPRequest) evt.getRequest(); + // 回复200 OK + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 心跳回复: {}", e.getMessage()); + } + + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); + if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setIp(remoteAddressInfo.getIp()); + } + if (device.getKeepaliveTime() == null) { + device.setKeepaliveIntervalTime(60); + }else { + long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); + device.setKeepaliveIntervalTime(new Long(System.currentTimeMillis()/1000-lastTime).intValue()); + } + + device.setKeepaliveTime(DateUtil.getNow()); + + if (device.getOnline() == 1) { + if(StrUtil.isNotEmpty(device.getModel())&&device.getModel().equals("StationNode")){ + device.setOnline(1); + deviceService.online(device, null); + }else{ +// logger.info("接收摄像机设备心跳 {}", device.getDeviceId()); + deviceService.updateDevice(device); + } + }else { + // 对于已经离线的设备判断他的注册是否已经过期 + if (!deviceService.expire(device)){ + device.setOnline(0); + deviceService.online(device, null); + } + } + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果三次心跳失败,则设置设备离线 + dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3); + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + // 不会收到上级平台的心跳信息 + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java new file mode 100644 index 0000000..333c2cc --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java @@ -0,0 +1,128 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.cmd; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.bean.SsrcTransaction; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.notify.NotifyMessageHandler; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.HookSubscribeFactory; +import com.yfd.monitor.media.zlm.dto.HookSubscribeForStreamChange; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +/** + * 媒体通知 + */ +@Component +public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(MediaStatusNotifyMessageHandler.class); + private final String cmdType = "MediaStatus"; + + @Autowired + private NotifyMessageHandler notifyMessageHandler; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storage; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Override + public void afterPropertiesSet() throws Exception { + notifyMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像流推送完毕,回复200OK: {}", e.getMessage()); + } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); + String NotifyType =getText(rootElement, "NotifyType"); + if ("121".equals(NotifyType)){ + logger.info("[录像流]推送完毕,收到关流通知"); + // 查询是设备 + StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId()); + if (streamInfo != null) { + // 设置进度100% + streamInfo.setProgress(1); + redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId()); + } + + // 先从会话内查找 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + + try { + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), null, callIdHeader.getCallId()); + } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) { + logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage()); + } + // 去除监听流注销自动停止下载的监听 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcTransaction.getStream(), false, "rtsp", ssrcTransaction.getMediaServerId()); + subscribe.removeSubscribe(hookSubscribe); + + // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null); + if (sendRtpItem != null) { + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + if (parentPlatform == null) { + logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId()); + return; + } +// try { +// sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem); +// } catch (SipException | InvalidArgumentException | ParseException e) { +// logger.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage()); +// } + } + } + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/QueryMessageByparentHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/QueryMessageByparentHandler.java new file mode 100644 index 0000000..dddd181 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/QueryMessageByparentHandler.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 查询指令 + * 命令类型: 设备状态, 设备目录信息, 设备信息, 文件目录检索(TODO), 报警(TODO), 设备配置(TODO), 设备预置位(TODO), 移动设备位置数据(TODO) + */ +@Component +public class QueryMessageByparentHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "SIP_XML"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/QueryMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/QueryMessageHandler.java new file mode 100644 index 0000000..df60898 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/QueryMessageHandler.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 查询指令 + * 命令类型: 设备状态, 设备目录信息, 设备信息, 文件目录检索(TODO), 报警(TODO), 设备配置(TODO), 设备预置位(TODO), 移动设备位置数据(TODO) + */ +@Component +public class QueryMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Query"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java new file mode 100644 index 0000000..302a9cb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java @@ -0,0 +1,68 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageHandler; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +@Component +public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(AlarmQueryMessageHandler.class); + private final String cmdType = "Alarm"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SipConfig config; + + @Autowired + private EventPublisher publisher; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + logger.info("不支持alarm查询"); + try { + responseAck((SIPRequest) evt.getRequest(), Response.NOT_FOUND, "not support alarm query"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 alarm查询回复200OK: {}", e.getMessage()); + } + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java new file mode 100644 index 0000000..fbc620e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java @@ -0,0 +1,55 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageHandler; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +@Component +public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceInfoQueryMessageHandler.class); + private final String cmdType = "DeviceInfo"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + @Autowired + private IVideoManagerStorage storager; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java new file mode 100644 index 0000000..aafec39 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java @@ -0,0 +1,85 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageHandler; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +@Component +public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceStatusQueryMessageHandler.class); + private final String cmdType = "DeviceStatus"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SipConfig config; + + @Autowired + private EventPublisher publisher; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + logger.info("接收到DeviceStatus查询消息"); + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage()); + } + String sn = rootElement.element("SN").getText(); + String channelId = getText(rootElement, "DeviceID"); + DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); + if (deviceChannel ==null){ + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + return; + } + try { + cmderFroPlatform.deviceStatusResponse(parentPlatform,channelId, sn, fromHeader.getTag(),deviceChannel.getStatus()==1); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复: {}", e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java new file mode 100644 index 0000000..d5ea132 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java @@ -0,0 +1,146 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.record.RecordEndEventListener; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageHandler; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.dto.ChannelSourceInfo; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.List; + +@Component +public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(RecordInfoQueryMessageHandler.class); + private final String cmdType = "RecordInfo"; + + @Autowired + private QueryMessageHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SIPCommander commander; + + @Autowired + private RecordEndEventListener recordEndEventListener; + + @Autowired + private SipConfig config; + + @Autowired + private EventPublisher publisher; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + SIPRequest request = (SIPRequest) evt.getRequest(); + Element snElement = rootElement.element("SN"); + int sn = Integer.parseInt(snElement.getText()); + Element deviceIDElement = rootElement.element("DeviceID"); + String channelId = deviceIDElement.getText(); + Element startTimeElement = rootElement.element("StartTime"); + String startTime = null; + if (startTimeElement != null) { + startTime = startTimeElement.getText(); + } + Element endTimeElement = rootElement.element("EndTime"); + String endTime = null; + if (endTimeElement != null) { + endTime = endTimeElement.getText(); + } + Element secrecyElement = rootElement.element("Secrecy"); + int secrecy = 0; + if (secrecyElement != null) { + secrecy = Integer.parseInt(secrecyElement.getText().trim()); + } + String type = "all"; + Element typeElement = rootElement.element("Type"); + if (typeElement != null) { + type = typeElement.getText(); + } + // 确认是直播还是国标, 国标直接请求下级,直播请求录像管理服务 + List channelSources = storager.getChannelSource(parentPlatform.getServerGBId(), channelId); + + if (channelSources.get(0).getCount() > 0) { // 国标 + // 向国标设备请求录像数据 + Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); + DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); + // 接收录像数据 + recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{ + try { + cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, request.getFromTag(), recordInfo); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage()); + } + }); + try { + commander.recordInfoQuery(device, channelId, DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTime), + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> { + // 回复200 OK + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage()); + } + }),(eventResult -> { + // 查询失败 + try { + responseAck(request, eventResult.statusCode, eventResult.msg); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage()); + } + })); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 录像查询: {}", e.getMessage()); + } + + }else if (channelSources.get(1).getCount() > 0) { // 直播流 + // TODO + try { + responseAck(request, Response.NOT_IMPLEMENTED); // 回复未实现 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 录像查询: {}", e.getMessage()); + } + }else { // 错误的请求 + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 录像查询: {}", e.getMessage()); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/AlarmQueryByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/AlarmQueryByParentMessageHandler.java new file mode 100644 index 0000000..41972d8 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/AlarmQueryByParentMessageHandler.java @@ -0,0 +1,71 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import java.text.ParseException; + +@Component +public class AlarmQueryByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(AlarmQueryByParentMessageHandler.class); + private final String cmdType = "Request_History_Alarm"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SIPSender sipSender; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException { + //处理上级系统发出的目录查询要求 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + Element itemElement = rootElement.element("Item"); + String Code = itemElement.attributeValue("Code"); + String UserCode = itemElement.attributeValue("UserCode"); + String Type = itemElement.attributeValue("Type"); + String BeginTime = itemElement.attributeValue("BeginTime"); + String EndTime = itemElement.attributeValue("EndTime"); + String Level = itemElement.attributeValue("Level"); + String FromIndex = itemElement.attributeValue("FromIndex"); + String ToIndex = itemElement.attributeValue("ToIndex"); + String resultXml=cmderFroPlatform.queryHistoryAlarm(parentPlatform,Code,UserCode,Type,BeginTime,EndTime,Level,FromIndex,ToIndex); + SIPRequest request = (SIPRequest) evt.getRequest(); + SIPResponse response = responseXmlAck(request, resultXml, parentPlatform, 0); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/CatalogQueryByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/CatalogQueryByParentMessageHandler.java new file mode 100644 index 0000000..a77d2c9 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/CatalogQueryByParentMessageHandler.java @@ -0,0 +1,78 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageHandler; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +@Component +public class CatalogQueryByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(CatalogQueryByParentMessageHandler.class); + private final String cmdType = "Request_Resource"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SIPSender sipSender; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException { + //处理上级系统发出的目录查询要求 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + Element itemElement = rootElement.element("Item"); + String Code = itemElement.attributeValue("Code"); + String FromIndex = itemElement.attributeValue("FromIndex"); + String ToIndex = itemElement.attributeValue("ToIndex"); + String resultXml = cmderFroPlatform.queryCatalog(parentPlatform, Code, FromIndex, ToIndex, fromHeader.getTag()); + SIPRequest request = (SIPRequest) evt.getRequest(); + logger.error("上级B接口请求获取资源" + request); + SIPResponse response = responseXmlAck(request, resultXml, parentPlatform, 0); +// logger.error("返回B接口资源资源" + response); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/Control_cameraByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/Control_cameraByParentMessageHandler.java new file mode 100644 index 0000000..833fe93 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/Control_cameraByParentMessageHandler.java @@ -0,0 +1,305 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + +import cn.hutool.core.util.StrUtil; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.DeviceMapper; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import java.text.ParseException; +import java.util.List; + +@Component +public class Control_cameraByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(Control_cameraByParentMessageHandler.class); + private final String cmdType = "Control_Camera"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + + @Autowired + private SIPCommander sipCommander; + + @Autowired + private SIPSender sipSender; + + @Autowired + private DeviceChannelMapper channelMapper; + @Autowired + private DeviceMapper deviceMapper; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException { + logger.error("==================================================处理上级系统发出的设备控制指令"); + //处理上级系统发出的设备控制指令 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + Element itemElement = rootElement.element("Item"); + String Code = itemElement.attributeValue("Code"); + String Command = itemElement.attributeValue("Command"); + String CommandPara1 = StrUtil.isEmpty(itemElement.attributeValue("CommandPara1")) ? "0" : itemElement.attributeValue("CommandPara1"); + String CommandPara2 = StrUtil.isEmpty(itemElement.attributeValue("CommandPara2")) ? "0" : itemElement.attributeValue("CommandPara2"); + String CommandPara3 = StrUtil.isEmpty(itemElement.attributeValue("CommandPara3")) ? "0" : itemElement.attributeValue("CommandPara3"); + + String cmdString = qgdw2019CmdString(Integer.parseInt(Command), Integer.parseInt(CommandPara1), Integer.parseInt(CommandPara2), Integer.parseInt(CommandPara3)); + + String channelid = Code; + String deviceId = ""; + //获取一个默认通道 + List channels = channelMapper.queryChannelList(Code); + logger.error("==================================================进入云台方法"+channels.size()); + if (channels.size() > 0) { + DeviceChannel deviceChannel = channels.get(0); + channelid = deviceChannel.getChannelId(); + deviceId = deviceChannel.getDeviceId(); + } + sipCommander.fronEndCmd(deviceMapper.getDeviceByDeviceId(deviceId), channelid, cmdString, null, eventResult -> { + SIPRequest request = (SIPRequest) evt.getRequest(); + SIPResponse response = null; + try { + response = responseAck(request, 200); + } catch (SipException | InvalidArgumentException | ParseException e) { + e.printStackTrace(); + } + try { + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } catch (SipException | ParseException e) { + e.printStackTrace(); + } + }); + + } + + public static String qgdw2019CmdString(int Command, int CommandPara1, int CommandPara2, int CommandPara3) { +// 0x0101 转换为 257(光圈关停止) +// 0x0102 转换为 258(光圈关) +// 0x0103 转换为 259(光圈开) +// 0x0104 转换为 260(光圈开停止) +// 0x0201 转换为 513(近聚焦停止) +// 0x0202 转换为 514(近聚焦) +// 0x0203 转换为 515(远聚焦停止) +// 0x0204 转换为 516(远聚焦) +// 0x0301 转换为 769(缩小停止) +// 0x0302 转换为 770(缩小) +// 0x0303 转换为 771(放大停止) +// 0x0304 转换为 772(放大) +// 0x0401 转换为 1025(向上停止) +// 0x0402 转换为 1026(向上) +// 0x0403 转换为 1027(向下停止) +// 0x0404 转换为 1028(向下) +// 0x0501 转换为 1281(右转停止) +// 0x0502 转换为 1282(右转) +// 0x0503 转换为 1283(左转停止) +// 0x0504 转换为 1284(左转) +// 0x0601 转换为 1537(预置位保存) +// 0x0602 转换为 1538(预置位调用) +// 0x0603 转换为 1539(预置位删除) +// 0x0701 转换为 1793(左上方向运动停止) +// 0x0702 转换为 1794(左上方向运动) +// 0x0703 转换为 1795(左下方向运动停止) +// 0x0704 转换为 1796(左下方向运动) +// 0x0801 转换为 2049(右上方向运动停止) +// 0x0802 转换为 2050(右上方向运动) +// 0x0803 转换为 2051(右下方向运动停止) +// 0x0804 转换为 2052(右下方向运动) +// 0x0901 转换为 2305(停止当前动作) +// 0x0a01 转换为 2561(雨刷开) +// 0x0a02 转换为 2562(雨刷关) +// 0x0b01 转换为 2817(灯亮) +// 0x0b02 转换为 2818(灯灭) +// 0x0c01 转换为 3073(加热开) +// 0x0c02 转换为 3074(加热关) +// 0x0d01 转换为 3329(红外开) +// 0x0d02 转换为 3330(红外关) +// 0x0e01 转换为 3585(线性扫描开始) +// 0x0e02 转换为 3586(线性扫描停止) +// 0x0f01 转换为 3841(轨迹巡航开始) +// 0x0f02 转换为 3842(轨迹巡航停止) +// 0x1001 转换为 4097(预置位巡航开始) +// 0x1002 转换为 4098(预置位巡航停止) +// 0x1101 转换为 4353(云台锁定) +// 0x1102 转换为 4354(云台解锁) + String cmdstring = ""; + switch (Command) { + case 769://0x0301 转换为 769(缩小停止) + cmdstring = cmdString(0, 0, 0, 0, 0, 0); + break; + case 770://0x0302 转换为 770(缩小) + cmdstring = cmdString(0, 0, 1, 0, 0, 16); + break; + case 771://0x0303 转换为 771(放大停止) + cmdstring = cmdString(0, 0, 0, 0, 0, 0); + break; + case 772://0x0304 转换为 772(放大) + cmdstring = cmdString(0, 0, 2, 0, 0, 16); + break; + case 1025://0x0401 转换为 1025(向上停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1026://0x0402 转换为 1026(向上) + cmdstring = cmdString(1, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1027://0x0403 转换为 1027(向下停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1028://0x0404 转换为 1028(向下) + cmdstring = cmdString(2, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1281://0x0501 转换为 1281(右转停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1282://0x0502 转换为 1282(右转) + cmdstring = cmdString(0, 2, 0, CommandPara1, CommandPara2, 0); + break; + case 1283://0x0503 转换为 1283(左转停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1284://0x0504 转换为 1284(左转) + cmdstring = cmdString(0, 1, 0, CommandPara1, CommandPara2, 0); + break; + case 1537://0x0601 转换为 1537(预置位保存) + cmdstring = frontEndCmdString(129, 0, CommandPara1, 0); + break; + case 1538://0x0602 转换为 1538(预置位调用) + cmdstring = frontEndCmdString(130, 0, CommandPara1, 0); + break; + case 1539://0x0603 转换为 1539(预置位删除) + cmdstring = frontEndCmdString(131, 0, CommandPara1, 0); + break; + case 1793://0x0701 转换为 1793(左上方向运动停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1794://0x0702 转换为 1794(左上方向运动) + cmdstring = cmdString(1, 1, 0, CommandPara1, CommandPara2, 0); + break; + case 1795://0x0703 转换为 1795(左下方向运动停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 1796://0x0704 转换为 1796(左下方向运动) + cmdstring = cmdString(2, 1, 0, CommandPara1, CommandPara2, 0); + break; + case 2049://0x0801 转换为 2049(右上方向运动停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 2050://0x0802 转换为 2050(右上方向运动) + cmdstring = cmdString(1, 2, 0, CommandPara1, CommandPara2, 0); + break; + case 2051://0x0803 转换为 2051(右下方向运动停止) + cmdstring = cmdString(0, 0, 0, CommandPara1, CommandPara2, 0); + break; + case 2052://0x0804 转换为 2052(右下方向运动) + cmdstring = cmdString(2, 2, 0, CommandPara1, CommandPara2, 0); + break; + } + return cmdstring; + } + + + /** + * 云台指令码计算 + * + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed1 镜头上下移动速度 + * @param moveSpeed2 镜头左右移动速度 + * @param zoomSpeed 镜头缩放速度 + */ + public static String cmdString(int upDown, int leftRight, int inOut, int moveSpeed1, int moveSpeed2, int zoomSpeed) { + int cmdCode = 0; + if (leftRight == 2) { + cmdCode |= 0x01; // 右移 + } else if (leftRight == 1) { + cmdCode |= 0x02; // 左移 + } + if (upDown == 2) { + cmdCode |= 0x04; // 下移 + } else if (upDown == 1) { + cmdCode |= 0x08; // 上移 + } + if (inOut == 2) { + cmdCode |= 0x10; // 放大 + } else if (inOut == 1) { + cmdCode |= 0x20; // 缩小 + } + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", moveSpeed1 * 28); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", moveSpeed2 * 28); + builder.append(strTmp, 0, 2); + //优化zoom低倍速下的变倍速率 + if ((zoomSpeed > 0) && (zoomSpeed < 16)) { + zoomSpeed = 16; + } + strTmp = String.format("%X", zoomSpeed); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed1 * 28 + moveSpeed2 * 28 + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 云台指令码计算 + * + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) { + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter1); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter2); + builder.append(strTmp, 0, 2); + //优化zoom变倍速率 + if ((combineCode2 > 0) && (combineCode2 < 16)) { + combineCode2 = 16; + } + strTmp = String.format("%X", combineCode2); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/Control_playbackByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/Control_playbackByParentMessageHandler.java new file mode 100644 index 0000000..56c372c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/Control_playbackByParentMessageHandler.java @@ -0,0 +1,66 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sdp.SdpParseException; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import java.text.ParseException; + +@Component +public class Control_playbackByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(Control_playbackByParentMessageHandler.class); + private final String cmdType = "Control_Playback"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IPlayService playService; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException, SdpParseException { + //处理上级系统发出的设备控制指令 + SIPRequest request = (SIPRequest) evt.getRequest(); + String channelId = SipUtils.getChannelIdFromRequest(request); + String requesterId = SipUtils.getUserIdFromFromHeader(request); + Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); + String contentString = new String(request.getRawContent()); + + //playService.pauseRtp(streamId); + System.out.println("this is test"); + + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/RecordQueryByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/RecordQueryByParentMessageHandler.java new file mode 100644 index 0000000..b4fc2a1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/RecordQueryByParentMessageHandler.java @@ -0,0 +1,69 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import java.text.ParseException; + +@Component +public class RecordQueryByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(RecordQueryByParentMessageHandler.class); + private final String cmdType = "Request_History_Video"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SIPSender sipSender; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException { + //处理上级系统发出的目录查询要求 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + Element itemElement = rootElement.element("Item"); + String Code = itemElement.attributeValue("Code"); + String UserCode = itemElement.attributeValue("UserCode"); + String Type = itemElement.attributeValue("Type"); + String BeginTime = itemElement.attributeValue("BeginTime"); + String EndTime = itemElement.attributeValue("EndTime"); + String FromIndex = itemElement.attributeValue("FromIndex"); + String ToIndex = itemElement.attributeValue("ToIndex"); + String resultXml=cmderFroPlatform.queryDeviceRecord(parentPlatform,Code,UserCode,Type,BeginTime,EndTime,FromIndex,ToIndex); + SIPRequest request = (SIPRequest) evt.getRequest(); + SIPResponse response = responseXmlAck(request, resultXml, parentPlatform, 0); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/ScreenControlByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/ScreenControlByParentMessageHandler.java new file mode 100644 index 0000000..6d58f46 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/ScreenControlByParentMessageHandler.java @@ -0,0 +1,82 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import java.text.ParseException; + +/** + * @Date: 2024/5/23 10:14 + * @Description: + */ +@Component +public class ScreenControlByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(AlarmQueryByParentMessageHandler.class); + private final String cmdType = "DragDrop_Camera"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SIPSender sipSender; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException { + //处理上级系统发出的目录查询要求 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + Element itemElement = rootElement.element("Item"); + String Code = itemElement.attributeValue("Code"); + String UserCode = itemElement.attributeValue("Type"); + String Type = itemElement.attributeValue("WindowLength"); + String BeginTime = itemElement.attributeValue("WindowWidth"); + String EndTime = itemElement.attributeValue("CenterX"); + String Level = itemElement.attributeValue("CenterY"); + String FromIndex = itemElement.attributeValue("BoxLength"); + String ToIndex = itemElement.attributeValue("BoxWidth"); + String PointNum = itemElement.attributeValue("PointNum"); + String Point = itemElement.attributeValue("Point"); + String Point_X = itemElement.attributeValue("Point_X"); + String Point_Y = itemElement.attributeValue("Point_Y"); + String ThermName = itemElement.attributeValue("ThermName"); + String Distance = itemElement.attributeValue("Distance"); + String Emissivity = itemElement.attributeValue("Emissivity"); + String Preset = itemElement.attributeValue("Preset"); + String RuleID = itemElement.attributeValue("RuleID"); + String Enable = itemElement.attributeValue("Enable"); + String resultXml = ""; + SIPRequest request = (SIPRequest) evt.getRequest(); + SIPResponse response = responseXmlAck(request, resultXml, parentPlatform, 0); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/SnapImageByParentMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/SnapImageByParentMessageHandler.java new file mode 100644 index 0000000..768ffcf --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/query/cmd_parent/SnapImageByParentMessageHandler.java @@ -0,0 +1,132 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.cmd_parent; + + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.query.QueryMessageByparentHandler; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.FromHeader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Component +public class SnapImageByParentMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(SnapImageByParentMessageHandler.class); + private final String cmdType = "Camera_Snap"; + + @Autowired + private QueryMessageByparentHandler queryMessageHandler; + + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + + + @Autowired + private SIPSender sipSender; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) throws InvalidArgumentException, ParseException, SipException { + //(1)回复上级系统200 ok + SIPRequest request = (SIPRequest) evt.getRequest(); + SIPResponse response = this.responseAck(request, 200); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + + //(2)获取请求参数,并调用截图功能 + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + Element itemElement = rootElement.element("Item"); + String deviceid = itemElement.attributeValue("Code"); + String PicServer = itemElement.attributeValue("PicServer"); + String SnapType = itemElement.attributeValue("SnapType"); + String Range = itemElement.attributeValue("Range"); + String Interval = itemElement.attributeValue("Interval");//抓拍间隔,单位秒 + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); + if(SnapType.equals("0")){ //0 单次推送方式,设备抓拍当前图片且推送 + cmderFroPlatform.snapImageAndsendNoticeMsg(deviceid,PicServer,parentPlatform,sipTransactionInfo); + }else {//1 多次推送方式,设备以一定时间间隔 + String[] jobtimes={}; + if(Range.contains(","))jobtimes=Range.split(","); + if(Range.contains("-"))jobtimes=Range.split("-"); + int startTime= Integer.parseInt(jobtimes[0]); + int endTime= Integer.parseInt(jobtimes[1]); + int intervalInSeconds=Integer.parseInt(Interval); + executeDailyTask(startTime,endTime,intervalInSeconds,deviceid,PicServer,parentPlatform,sipTransactionInfo); + } + } + + + private void executeDailyTask(int startTime, int endTime, int intervalInSeconds,String deviceid,String PicServer,ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) { + int interval=endTime-startTime; + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.add(Calendar.SECOND, startTime); + Date startDate = calendar.getTime(); + calendar.add(Calendar.SECOND, interval); + Date endDate = calendar.getTime(); + + // 计算首次执行的延迟时间 + long initialDelay = startDate.getTime() - System.currentTimeMillis(); + + // 如果开始时间已过,则计算下一个执行时间 + if (initialDelay < 0) { + initialDelay = intervalInSeconds - (Math.abs(initialDelay) % intervalInSeconds); + } + // 执行任务 + scheduler.scheduleAtFixedRate(() -> { // 执行你的任务逻辑 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + System.out.println("snap image Task executed at " + sdf.format(new Date())); + try { + cmderFroPlatform.snapImageAndsendNoticeMsg(deviceid,PicServer,parentPlatform,sipTransactionInfo); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + }, initialDelay, intervalInSeconds, TimeUnit.SECONDS); + + // 设置定时任务结束时间 + scheduler.schedule(() -> scheduler.shutdown(), endDate.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + } + + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/ResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/ResponseMessageHandler.java new file mode 100644 index 0000000..532a877 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/ResponseMessageHandler.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response; + +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageHandlerAbstract; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.MessageRequestProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 命令类型: 请求动作的应答 + * 命令类型: 设备控制, 报警通知, 设备目录信息查询, 目录信息查询, 目录收到, 设备信息查询, 设备状态信息查询 ...... + */ +@Component +public class ResponseMessageHandler extends MessageHandlerAbstract implements InitializingBean { + + private final String messageType = "Response"; + + @Autowired + private MessageRequestProcessor messageRequestProcessor; + + @Override + public void afterPropertiesSet() throws Exception { + messageRequestProcessor.addHandler(messageType, this); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java new file mode 100644 index 0000000..c9040f1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java @@ -0,0 +1,58 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; + +@Component +public class AlarmResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(AlarmResponseMessageHandler.class); + private final String cmdType = "Alarm"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getText(); + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + device.getDeviceId() + channelId; + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java new file mode 100644 index 0000000..c8c3ee4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java @@ -0,0 +1,73 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +@Component +public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(BroadcastResponseMessageHandler.class); + private final String cmdType = "Broadcast"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + try { + String channelId = getText(rootElement, "DeviceID"); + String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId; + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + // 此处是对本平台发出Broadcast指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(rootElement, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + + + } catch (ParseException | SipException | InvalidArgumentException e) { + logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java new file mode 100644 index 0000000..e1d6571 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java @@ -0,0 +1,175 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import cn.hutool.core.util.StrUtil; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.session.CatalogDataCatch; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 目录查询的回复 + */ +@Component +public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(CatalogResponseMessageHandler.class); + private final String cmdType = "Catalog"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private CatalogDataCatch catalogDataCatch; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(new HandlerCatchData(evt, device, element)); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 目录查询回复: {}", e.getMessage()); + } + // 如果不为空则说明已经开启消息处理 + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + // 全局异常捕获,保证下一条可以得到处理 + try { + HandlerCatchData take = taskQueue.poll(); + Element rootElement = null; + try { + rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset()); + } catch (DocumentException e) { + logger.error("[xml解析] 失败: ", e); + continue; + } + if (rootElement == null) { + logger.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest()); + continue; + } + Element deviceListElement = rootElement.element("DeviceList"); + Element sumNumElement = rootElement.element("SumNum"); + Element snElement = rootElement.element("SN"); + int sumNum = Integer.parseInt(sumNumElement.getText()); + + if (sumNum == 0) { + logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); + // 数据已经完整接收 + storager.cleanChannelsForDevice(take.getDevice().getDeviceId()); + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); + } else { + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + List channelList = new ArrayList<>(); + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null); + deviceChannel = SipUtils.updateGps(deviceChannel, device.getGeoCoordSys()); + deviceChannel.setDeviceId(take.getDevice().getDeviceId()); + String address = deviceChannel.getAddress(); + if (StrUtil.isNotBlank(address) && !"Address".equals(address) && !"axy".equals(address) && StrUtil.isBlank(deviceChannel.getParentId())) { + deviceChannel.setParentId(deviceChannel.getDeviceId()); + } + channelList.add(deviceChannel); + } + int sn = Integer.parseInt(snElement.getText()); + catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), channelList); + logger.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.get(take.getDevice().getDeviceId()) == null ? 0 : catalogDataCatch.get(take.getDevice().getDeviceId()).size(), sumNum); + if (catalogDataCatch.get(take.getDevice().getDeviceId()).size() == sumNum) { + // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理, + // 目前支持设备通道上线通知时和设备上线时向上级通知 + boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId())); + if (!resetChannelsResult) { + String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条"; + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg); + } else { + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); + } + } + } + + } + }catch (Exception e) { + logger.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest()); + logger.error("[收到通道] 异常内容: ", e); + } + } + }); + } + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + } + + public SyncStatus getChannelSyncProgress(String deviceId) { + if (catalogDataCatch.get(deviceId) == null) { + return null; + } else { + return catalogDataCatch.getSyncStatus(deviceId); + } + } + + public boolean isSyncRunning(String deviceId) { + if (catalogDataCatch.get(deviceId) == null) { + return false; + } else { + return catalogDataCatch.isSyncRunning(deviceId); + } + } + + public void setChannelSyncReady(Device device, int sn) { + catalogDataCatch.addReady(device, sn); + } + + public void setChannelSyncEnd(String deviceId, String errorMsg) { + catalogDataCatch.setChannelSyncEnd(deviceId, errorMsg); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java new file mode 100644 index 0000000..e843a47 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java @@ -0,0 +1,79 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +@Component +public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(ConfigDownloadResponseMessageHandler.class); + private final String cmdType = "ConfigDownload"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private EventPublisher publisher; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + String channelId = getText(element, "DeviceID"); + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId; + try { + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 设备配置查询: {}", e.getMessage()); + } + // 此处是对本平台发出DeviceControl指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(element, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + // 不会收到上级平台的心跳信息 + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java new file mode 100644 index 0000000..b2d9d17 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java @@ -0,0 +1,58 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.RequestEvent; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +@Component +public class DeviceConfigResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceConfigResponseMessageHandler.class); + private final String cmdType = "DeviceConfig"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + JSONObject json = new JSONObject(); + XmlUtil.node2Json(element, json); + String channelId = getText(element, "DeviceID"); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + device.getDeviceId() + channelId; + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java new file mode 100644 index 0000000..c06a916 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java @@ -0,0 +1,71 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; + +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +@Component +public class DeviceControlResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceControlResponseMessageHandler.class); + private final String cmdType = "DeviceControl"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + // 此处是对本平台发出DeviceControl指令的应答 + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 设备控制: {}", e.getMessage()); + } + JSONObject json = new JSONObject(); + String channelId = getText(element, "DeviceID"); + XmlUtil.node2Json(element, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + device.getDeviceId() + channelId; + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java new file mode 100644 index 0000000..0c84369 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java @@ -0,0 +1,110 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + + +@Component +public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceInfoResponseMessageHandler.class); + private final String cmdType = "DeviceInfo"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + + @Autowired + private IDeviceService deviceService; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + logger.debug("接收到DeviceInfo应答消息"); + // 检查设备是否存在, 不存在则不回复 + if (device == null || device.getOnline() == 0) { + logger.warn("[接收到DeviceInfo应答消息,但是设备已经离线]:" + (device != null ? device.getDeviceId():"" )); + return; + } + SIPRequest request = (SIPRequest) evt.getRequest(); + try { + rootElement = getRootElement(evt, device.getCharset()); + + if (rootElement == null) { + logger.warn("[ 接收到DeviceInfo应答消息 ] content cannot be null, {}", evt.getRequest()); + try { + responseAck((SIPRequest) evt.getRequest(), Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] DeviceInfo应答消息 BAD_REQUEST: {}", e.getMessage()); + } + return; + } + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getTextTrim(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICEINFO + device.getDeviceId() + channelId; + device.setName(getText(rootElement, "DeviceName")); + + device.setManufacturer(getText(rootElement, "Manufacturer")); + device.setModel(getText(rootElement, "Model")); + device.setFirmware(getText(rootElement, "Firmware")); + if (ObjectUtils.isEmpty(device.getStreamMode())) { + device.setStreamMode("UDP"); + } + deviceService.updateDevice(device); + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(device); + deferredResultHolder.invokeAllResult(msg); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + try { + // 回复200 OK + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage()); + } + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java new file mode 100644 index 0000000..a57cc2a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java @@ -0,0 +1,89 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.XmlUtil; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +@Component +public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(DeviceStatusResponseMessageHandler.class); + private final String cmdType = "DeviceStatus"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + logger.info("接收到DeviceStatus应答消息"); + // 检查设备是否存在, 不存在则不回复 + if (device == null) { + return; + } + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 设备状态应答回复200OK: {}", e.getMessage()); + } + Element deviceIdElement = element.element("DeviceID"); + Element onlineElement = element.element("Online"); + String channelId = deviceIdElement.getText(); + JSONObject json = new JSONObject(); + XmlUtil.node2Json(element, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + String text = onlineElement.getText(); + if ("ONLINE".equalsIgnoreCase(text.trim())) { + deviceService.online(device, null); + }else { + deviceService.offline(device.getDeviceId(), "设备状态查询结果:" + text.trim()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId()); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java new file mode 100644 index 0000000..2298f53 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java @@ -0,0 +1,151 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.gdw2019.utils.Coordtransform; +import com.yfd.monitor.gdw2019.utils.NumericUtil; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.GpsUtil; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +/** + * 移动设备位置数据查询回复 +* + */ +@Component +public class MobilePositionResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(MobilePositionResponseMessageHandler.class); + private final String cmdType = "MobilePosition"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + SIPRequest request = (SIPRequest) evt.getRequest(); + + try { + rootElement = getRootElement(evt, device.getCharset()); + if (rootElement == null) { + logger.warn("[ 移动设备位置数据查询回复 ] content cannot be null, {}", evt.getRequest()); + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 移动设备位置数据查询 BAD_REQUEST: {}", e.getMessage()); + } + return; + } + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); + if (!ObjectUtils.isEmpty(device.getName())) { + mobilePosition.setDeviceName(device.getName()); + } + mobilePosition.setDeviceId(device.getDeviceId()); + mobilePosition.setChannelId(getText(rootElement, "DeviceID")); + mobilePosition.setTime(getText(rootElement, "Time")); + mobilePosition.setLongitude(Double.parseDouble(getText(rootElement, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(getText(rootElement, "Latitude"))); + if (NumericUtil.isDouble(getText(rootElement, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(getText(rootElement, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(getText(rootElement, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(getText(rootElement, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(getText(rootElement, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(getText(rootElement, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + mobilePosition.setReportSource("Mobile Position"); + + // 更新device channel 的经纬度 + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(device.getDeviceId()); + deviceChannel.setChannelId(mobilePosition.getChannelId()); + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + + storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.getChannelId()); + jsonObject.put("longitude", mobilePosition.getLongitude()); + jsonObject.put("latitude", mobilePosition.getLatitude()); + jsonObject.put("altitude", mobilePosition.getAltitude()); + jsonObject.put("direction", mobilePosition.getDirection()); + jsonObject.put("speed", mobilePosition.getSpeed()); + redisCatchStorage.sendMobilePositionMsg(jsonObject); + //回复 200 OK + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage()); + } + + } catch (DocumentException e) { + logger.error("未处理的异常 ", e); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java new file mode 100644 index 0000000..93460ca --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java @@ -0,0 +1,120 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import com.yfd.monitor.gdw2019.bean.PresetQuerySipReq; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + +/** + * 设备预置位查询应答 + */ +@Component +public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(PresetQueryResponseMessageHandler.class); + private final String cmdType = "PresetQuery"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + SIPRequest request = (SIPRequest) evt.getRequest(); + + try { + Element rootElement = getRootElement(evt, device.getCharset()); + + if (rootElement == null) { + logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest()); + try { + responseAck(request, Response.BAD_REQUEST); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + } + return; + } + Element presetListNumElement = rootElement.element("PresetList"); + Element snElement = rootElement.element("SN"); + //该字段可能为通道或则设备的id + String deviceId = getText(rootElement, "DeviceID"); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId; + if (snElement == null || presetListNumElement == null) { + try { + responseAck(request, Response.BAD_REQUEST, "xml error"); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + } + return; + } + int sumNum = Integer.parseInt(presetListNumElement.attributeValue("Num")); + List presetQuerySipReqList = new ArrayList<>(); + if (sumNum > 0) { + for (Iterator presetIterator = presetListNumElement.elementIterator(); presetIterator.hasNext(); ) { + Element itemListElement = presetIterator.next(); + PresetQuerySipReq presetQuerySipReq = new PresetQuerySipReq(); + for (Iterator itemListIterator = itemListElement.elementIterator(); itemListIterator.hasNext(); ) { + // 遍历item + Element itemOne = itemListIterator.next(); + String name = itemOne.getName(); + String textTrim = itemOne.getTextTrim(); + if ("PresetID".equalsIgnoreCase(name)) { + presetQuerySipReq.setPresetId(textTrim); + } else { + presetQuerySipReq.setPresetName(textTrim); + } + } + presetQuerySipReqList.add(presetQuerySipReq); + } + } + RequestMessage requestMessage = new RequestMessage(); + requestMessage.setKey(key); + requestMessage.setData(presetQuerySipReqList); + deferredResultHolder.invokeAllResult(requestMessage); + try { + responseAck(request, Response.OK); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + } + } catch (DocumentException e) { + logger.error("[解析xml]失败: ", e); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java new file mode 100644 index 0000000..5be1993 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java @@ -0,0 +1,207 @@ +package com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd; + +import cn.hutool.core.date.DateUnit; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.event.request.SIPRequestProcessorParent; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.IMessageHandler; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.ResponseMessageHandler; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.UJson; +import gov.nist.javax.sip.message.SIPRequest; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.yfd.monitor.gdw2019.utils.XmlUtil.getText; + + +@Component +public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private final Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class); + private final String cmdType = "RecordInfo"; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + @Autowired + private EventPublisher eventPublisher; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Autowired + private RedisTemplate redisTemplate; + + private Long recordInfoTtl = 1800L; + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + try { + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + }catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); + } + taskExecutor.execute(()->{ + try { + String sn = getText(rootElement, "SN"); + String channelId = getText(rootElement, "DeviceID"); + RecordInfo recordInfo = new RecordInfo(); + recordInfo.setChannelId(channelId); + recordInfo.setDeviceId(device.getDeviceId()); + recordInfo.setSn(sn); + recordInfo.setName(getText(rootElement, "Name")); + String sumNumStr = getText(rootElement, "SumNum"); + int sumNum = 0; + if (!ObjectUtils.isEmpty(sumNumStr)) { + sumNum = Integer.parseInt(sumNumStr); + } + recordInfo.setSumNum(sumNum); + Element recordListElement = rootElement.element("RecordList"); + if (recordListElement == null || sumNum == 0) { + logger.info("无录像数据"); + recordInfo.setCount(sumNum); + eventPublisher.recordEndEventPush(recordInfo); + releaseRequest(device.getDeviceId(), sn,recordInfo); + } else { + Iterator recordListIterator = recordListElement.elementIterator(); + if (recordListIterator != null) { + List recordList = new ArrayList<>(); + // 遍历DeviceList + + while (recordListIterator.hasNext()) { + Element itemRecord = recordListIterator.next(); + Element recordElement = itemRecord.element("DeviceID"); + if (recordElement == null) { + logger.info("记录为空,下一个..."); + continue; + } + RecordItem record = new RecordItem(); + record.setDeviceId(getText(itemRecord, "DeviceID")); + record.setName(getText(itemRecord, "Name")); + record.setFilePath(getText(itemRecord, "FilePath")); + record.setFileSize(getText(itemRecord, "FileSize")); + record.setAddress(getText(itemRecord, "Address")); + + String startTimeStr = getText(itemRecord, "StartTime"); + record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr)); + + String endTimeStr = getText(itemRecord, "EndTime"); + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr)); + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 + : Integer.parseInt(getText(itemRecord, "Secrecy"))); + record.setType(getText(itemRecord, "Type")); + record.setRecorderId(getText(itemRecord, "RecorderID")); + recordList.add(record); + } + Map map = recordList.stream() + .filter(record -> record.getDeviceId() != null) + .collect(Collectors.toMap(record -> record.getStartTime()+ record.getEndTime(), UJson::writeJson)); + // 获取任务结果数据 + String resKey = VideoManagerConstants.REDIS_RECORD_INFO_RES_PRE + channelId + sn; + redisTemplate.opsForHash().putAll(resKey, map); + redisTemplate.expire(resKey, recordInfoTtl, TimeUnit.SECONDS); + String resCountKey = VideoManagerConstants.REDIS_RECORD_INFO_RES_COUNT_PRE + channelId + sn; + long incr = redisTemplate.opsForValue().increment(resCountKey, map.size()); + redisTemplate.expire(resCountKey, recordInfoTtl, TimeUnit.SECONDS); + recordInfo.setRecordList(recordList); + recordInfo.setCount(Math.toIntExact(incr)); + eventPublisher.recordEndEventPush(recordInfo); + if (incr < sumNum) { + return; + } + // 已接收完成 + List resList = + redisTemplate.opsForHash().entries(resKey).values().stream().map(e -> UJson.readJson(e.toString(), RecordItem.class)).collect(Collectors.toList()); + if (resList.size() < sumNum) { + return; + } + recordInfo.setRecordList(resList); + // 获取录像总时长 + if(sn.equals("9999")){ //录像时长 + long recordtimes=getRecordTimes(resList); + String RecordTimeKey = VideoManagerConstants.REDIS_RECORD_TIMES_PRE +channelId ; + redisTemplate.opsForValue().set(RecordTimeKey, recordtimes); + recordInfo.setRecordTimes(recordtimes); + } + + releaseRequest(device.getDeviceId(), sn, recordInfo); + } + } + } catch (Exception e) { + logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest()); + logger.error("[国标录像] 异常内容: ", e); + } + }); + } + + /********************************** + * 用途说明: 获取录像总时长 + * 参数说明 resList + * 返回值说明: long + ***********************************/ + private long getRecordTimes(List resList) { + long recordtimes = 0L; + for (RecordItem recordItem : resList) { + //增加了录像时长的计算(2023-06-20) + Date beginDate = cn.hutool.core.date.DateUtil.parse(recordItem.getStartTime(), "yyyy-MM-dd HH:mm:ss"); + Date endDate = cn.hutool.core.date.DateUtil.parse(recordItem.getEndTime(), "yyyy-MM-dd HH:mm:ss"); + long times = cn.hutool.core.date.DateUtil.between(beginDate, endDate, DateUnit.SECOND, true); + recordtimes += times; + } + return recordtimes; + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + + } + + public void releaseRequest(String deviceId, String sn, RecordInfo recordInfo) { + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; + // 对数据进行排序 + if (recordInfo != null && recordInfo.getRecordList() != null) { + Collections.sort(recordInfo.getRecordList()); + } else { + recordInfo.setRecordList(new ArrayList<>()); + } + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(recordInfo); + deferredResultHolder.invokeAllResult(msg); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/ISIPResponseProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/ISIPResponseProcessor.java new file mode 100644 index 0000000..9490690 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/ISIPResponseProcessor.java @@ -0,0 +1,15 @@ +package com.yfd.monitor.gdw2019.transmit.event.response; + +import javax.sip.ResponseEvent; + +/** + * @description:处理接收IPCamera发来的SIP协议响应消息 +* + * @date: 2020年5月3日 下午4:42:22 + */ +public interface ISIPResponseProcessor { + + void process(ResponseEvent evt); + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/SIPResponseProcessorAbstract.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/SIPResponseProcessorAbstract.java new file mode 100644 index 0000000..5bac55b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/SIPResponseProcessorAbstract.java @@ -0,0 +1,8 @@ +package com.yfd.monitor.gdw2019.transmit.event.response; + +import org.springframework.beans.factory.InitializingBean; + +public abstract class SIPResponseProcessorAbstract implements InitializingBean, ISIPResponseProcessor { + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/ByeResponseProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/ByeResponseProcessor.java new file mode 100644 index 0000000..cfe4f48 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/ByeResponseProcessor.java @@ -0,0 +1,50 @@ +package com.yfd.monitor.gdw2019.transmit.event.response.impl; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.event.response.ISIPResponseProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.ResponseEvent; + +/** + * @description: BYE请求响应器 +* + * @date: 2020年5月3日 下午5:32:05 + */ +@Component +public class ByeResponseProcessor implements InitializingBean, ISIPResponseProcessor { + + private final String method = "BYE"; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SipConfig config; + + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + /** + * 处理BYE响应 + * + * @param evt + */ + @Override + public void process(ResponseEvent evt) { + System.out.println(""); + // TODO Auto-generated method stub + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/CancelResponseProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/CancelResponseProcessor.java new file mode 100644 index 0000000..d715c56 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/CancelResponseProcessor.java @@ -0,0 +1,47 @@ +package com.yfd.monitor.gdw2019.transmit.event.response.impl; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.event.response.ISIPResponseProcessor; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.ResponseEvent; + +/** + * @description: CANCEL响应处理器 + * @date: 2021年11月5日 16:35 + */ +@Component +public class CancelResponseProcessor implements InitializingBean, ISIPResponseProcessor { + + private final String method = "CANCEL"; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SipConfig config; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + /** + * 处理CANCEL响应 + * + * @param evt + */ + @Override + public void process(ResponseEvent evt) { + // TODO Auto-generated method stub + + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/InviteResponseProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/InviteResponseProcessor.java new file mode 100644 index 0000000..6129e15 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/InviteResponseProcessor.java @@ -0,0 +1,120 @@ +package com.yfd.monitor.gdw2019.transmit.event.response.impl; + +import com.yfd.monitor.gdw2019.SipLayer; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.SIPSender; +import com.yfd.monitor.gdw2019.transmit.cmd.SIPRequestHeaderProvider; +import com.yfd.monitor.gdw2019.transmit.event.response.ISIPResponseProcessor; +import com.yfd.monitor.storager.IVideoManagerStorage; +import gov.nist.javax.sip.ResponseEventExt; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sdp.SdpFactory; +import javax.sdp.SdpParseException; +import javax.sdp.SessionDescription; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.SipFactory; +import javax.sip.address.SipURI; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.List; + +/** + * @description: 处理INVITE响应 + * @date: 2021年11月5日 16:40 + */ +@Component +public class InviteResponseProcessor implements InitializingBean, ISIPResponseProcessor { + + private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); + private final String method = "INVITE"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private SipLayer sipLayer; + + @Autowired + private SIPSender sipSender; + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPRequestHeaderProvider headerProvider; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + + + + /** + * 处理invite响应 + * + * @param evt 响应消息 + * @throws ParseException + */ + @Override + public void process(ResponseEvent evt ){ + logger.debug("接收到消息:" + evt.getResponse()); + try { + SIPResponse response = (SIPResponse)evt.getResponse(); + logger.info("=====================接收到消息====================" +response); + int statusCode = response.getStatusCode(); + // trying不会回复 + if (statusCode == Response.TRYING) { + } + // 成功响应 + // 下发ack + if (statusCode == Response.OK) { + ResponseEventExt event = (ResponseEventExt)evt; + + String contentString = new String(response.getRawContent()); + // jainSip不支持y=字段, 移除以解析。 + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + SessionDescription sdp; + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 + String substring = contentString.substring(0, contentString.indexOf("y=")); + sdp = SdpFactory.getInstance().createSessionDescription(substring); + } else { + sdp = SdpFactory.getInstance().createSessionDescription(contentString); + } + if (sdp != null) { + //注意解析----2024-02-13 + SipURI requestUri = + SipFactory.getInstance().createAddressFactory().createSipURI(sdp.getOrigin().getUsername() + , event.getRemoteIpAddress() + ":" + event.getRemotePort()); + Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), + requestUri, response); + logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), + event.getRemotePort()); + sipSender.transmitRequest(response.getLocalAddress().getHostAddress(), reqAck); +// if (response.toString().contains("Play")) { +// List parentPlatforms = storager.queryEnableParentPlatformList(true); +// for (ParentPlatform parentPlatform : parentPlatforms) { +// sipSender.transmitRequest(parentPlatform.getServerIP(), response); +// } +// } + } + + } + } catch (Exception e) { + logger.info("[点播回复ACK],异常:", e ); + } + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/RegisterResponseProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/RegisterResponseProcessor.java new file mode 100644 index 0000000..c24b196 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/response/impl/RegisterResponseProcessor.java @@ -0,0 +1,118 @@ +package com.yfd.monitor.gdw2019.transmit.event.response.impl; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.ParentPlatformCatch; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.bean.SubscribeHolder; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.gdw2019.transmit.event.response.ISIPResponseProcessor; +import com.yfd.monitor.service.IPlatformService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.dto.PlatformRegisterInfo; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import javax.sip.header.WWWAuthenticateHeader; +import javax.sip.message.Response; +import java.text.ParseException; + +/** + * @description:Register响应处理器 + * @date: 2020年5月3日 下午5:32:23 + */ +@Component +public class RegisterResponseProcessor implements InitializingBean, ISIPResponseProcessor { + + private final Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); + private final String method = "REGISTER"; + + @Autowired + private ISIPCommanderForPlatform sipCommanderForPlatform; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private IPlatformService platformService; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addResponseProcessor(method, this); + } + + /** + * 处理Register响应 + * + * @param evt 事件 + */ + @Override + public void process(ResponseEvent evt) { + SIPResponse response = (SIPResponse) evt.getResponse(); + String callId = response.getCallIdHeader().getCallId(); + PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId); + if (platformRegisterInfo == null) { + logger.error(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId)); + return; + } + + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformRegisterInfo.getPlatformId()); + if (parentPlatformCatch == null) { + logger.warn(String.format("[国标级联]收到注册/注销%S请求,平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(), platformRegisterInfo.getPlatformId())); + return; + } + + String action = platformRegisterInfo.isRegister() ? "注册" : "注销"; + logger.info(String.format("[国标级联]%s %S响应,%s ", action, response.getStatusCode(), platformRegisterInfo.getPlatformId())); + ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform(); + if (parentPlatform == null) { + logger.warn(String.format("[国标级联]收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformRegisterInfo.getPlatformId(), action, response.getStatusCode())); + return; + } + + if (response.getStatusCode() == Response.UNAUTHORIZED) { + WWWAuthenticateHeader www = (WWWAuthenticateHeader) response.getHeader(WWWAuthenticateHeader.NAME); + logger.info("权限信息" + www); + cn.hutool.json.JSONObject job = JSONUtil.parseObj(www); + redisCatchStorage.updatePlatformWWwInfo(parentPlatformCatch.getParentPlatform().getServerGBId(), job); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); + try { + sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, platformRegisterInfo.isRegister()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage()); + } + } else if (response.getStatusCode() == Response.OK) { + + if (platformRegisterInfo.isRegister()) { + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); + platformService.online(parentPlatform, sipTransactionInfo); + } else { + platformService.offline(parentPlatform, false); + } + + // 注册/注销成功移除缓存的信息 + redisCatchStorage.delPlatformRegisterInfo(callId); + } + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/timeout/ITimeoutProcessor.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/timeout/ITimeoutProcessor.java new file mode 100644 index 0000000..aff6a81 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/timeout/ITimeoutProcessor.java @@ -0,0 +1,7 @@ +package com.yfd.monitor.gdw2019.transmit.event.timeout; + +import javax.sip.TimeoutEvent; + +public interface ITimeoutProcessor { + void process(TimeoutEvent event); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/timeout/impl/TimeoutProcessorImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/timeout/impl/TimeoutProcessorImpl.java new file mode 100644 index 0000000..2ccde25 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/transmit/event/timeout/impl/TimeoutProcessorImpl.java @@ -0,0 +1,47 @@ +package com.yfd.monitor.gdw2019.transmit.event.timeout.impl; + +import com.yfd.monitor.conf.SystemInfoTimerTask; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.transmit.SIPProcessorObserver; +import com.yfd.monitor.gdw2019.transmit.event.timeout.ITimeoutProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sip.TimeoutEvent; +import javax.sip.header.CallIdHeader; + +@Component +public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor { + + private Logger logger = LoggerFactory.getLogger(TimeoutProcessorImpl.class); + + @Autowired + private SIPProcessorObserver processorObserver; + + @Autowired + private SipSubscribe sipSubscribe; + + @Override + public void afterPropertiesSet() throws Exception { + processorObserver.addTimeoutProcessor(this); + } + + @Override + public void process(TimeoutEvent event) { + try { + // TODO Auto-generated method stub + CallIdHeader callIdHeader = event.getClientTransaction().getDialog().getCallId(); + String callId = callIdHeader.getCallId(); + SipSubscribe.Event errorSubscribe = sipSubscribe.getErrorSubscribe(callId); + SipSubscribe.EventResult timeoutEventEventResult = new SipSubscribe.EventResult<>(event); + errorSubscribe.response(timeoutEventEventResult); + sipSubscribe.removeErrorSubscribe(callId); + sipSubscribe.removeOkSubscribe(callId); + } catch (Exception e) { + logger.error("[超时事件失败]: {}", e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/Coordtransform.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/Coordtransform.java new file mode 100644 index 0000000..d4442e3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/Coordtransform.java @@ -0,0 +1,125 @@ +package com.yfd.monitor.gdw2019.utils; + +/** + * 坐标转换 + * 一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具类 + * 参考https://github.com/wandergis/coordtransform 写的Java版本 + * @date 2016-03-18 + * @url https://github.com/xinconan/coordtransform + */ +public class Coordtransform { + + private static double x_PI = 3.14159265358979324 * 3000.0 / 180.0; + private static double PI = 3.1415926535897932384626; + private static double a = 6378245.0; + private static double ee = 0.00669342162296594323; + + /** + * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 + * 即 百度 转 谷歌、高德 + * @param bd_lon + * @param bd_lat + * @return Double[lon,lat] + */ + public static Double[] BD09ToGCJ02(Double bd_lon,Double bd_lat){ + double x = bd_lon - 0.0065; + double y = bd_lat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta); + arr[1] = z * Math.sin(theta); + return arr; + } + + /** + * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 + * 即谷歌、高德 转 百度 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToBD09(Double gcj_lon,Double gcj_lat){ + double z = Math.sqrt(gcj_lon * gcj_lon + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI); + double theta = Math.atan2(gcj_lat, gcj_lon) + 0.000003 * Math.cos(gcj_lon * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta) + 0.0065; + arr[1] = z * Math.sin(theta) + 0.006; + return arr; + } + + /** + * WGS84转GCJ02 + * @param wgs_lon + * @param wgs_lat + * @return Double[lon,lat] + */ + public static Double[] WGS84ToGCJ02(Double wgs_lon,Double wgs_lat){ + if(outOfChina(wgs_lon, wgs_lat)){ + return new Double[]{wgs_lon,wgs_lat}; + } + double dlat = transformlat(wgs_lon - 105.0, wgs_lat - 35.0); + double dlng = transformlng(wgs_lon - 105.0, wgs_lat - 35.0); + double radlat = wgs_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + Double[] arr = new Double[2]; + arr[0] = wgs_lon + dlng; + arr[1] = wgs_lat + dlat; + return arr; + } + + /** + * GCJ02转WGS84 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToWGS84(Double gcj_lon,Double gcj_lat){ + if(outOfChina(gcj_lon, gcj_lat)){ + return new Double[]{gcj_lon,gcj_lat}; + } + double dlat = transformlat(gcj_lon - 105.0, gcj_lat - 35.0); + double dlng = transformlng(gcj_lon - 105.0, gcj_lat - 35.0); + double radlat = gcj_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + double mglat = gcj_lat + dlat; + double mglng = gcj_lon + dlng; + return new Double[]{gcj_lon * 2 - mglng, gcj_lat * 2 - mglat}; + } + + private static Double transformlat(double lng, double lat) { + double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret; + } + + private static Double transformlng(double lng,double lat) { + double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret; + } + + /** + * outOfChina + * @描述: 判断是否在国内,不在国内则不做偏移 + * @param lng + * @param lat + * @return {boolean} + */ + private static boolean outOfChina(Double lng,Double lat) { + return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/MessageElement.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/MessageElement.java new file mode 100644 index 0000000..691a0e7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/MessageElement.java @@ -0,0 +1,16 @@ +package com.yfd.monitor.gdw2019.utils; + +import java.lang.annotation.*; + +/** + * @version 1.0 + * @date 2022/6/28 14:58 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MessageElement { + String value(); + + String subVal() default ""; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/NumericUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/NumericUtil.java new file mode 100644 index 0000000..3754d6f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/NumericUtil.java @@ -0,0 +1,46 @@ +package com.yfd.monitor.gdw2019.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数值格式判断和处理 + * + * @date 2021年1月27日 + */ +public class NumericUtil { + + private static Logger logger = LoggerFactory.getLogger(NumericUtil.class); + + /** + * 判断是否Double格式 + * @param str + * @return true/false + */ + public static boolean isDouble(String str) { + try { + Double num2 = Double.valueOf(str); +// logger.debug(num2 + " is a valid numeric string!"); + return true; + } catch (Exception e) { +// logger.debug(str + " is an invalid numeric string!"); + return false; + } + } + + /** + * 判断是否Double格式 + * @param str + * @return true/false + */ + public static boolean isInteger(String str) { + try { + int num2 = Integer.valueOf(str); +// logger.debug(num2 + " is an integer!"); + return true; + } catch (Exception e) { +// logger.debug(str + " is not an integer!"); + return false; + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/SipUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/SipUtils.java new file mode 100644 index 0000000..c35e939 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/SipUtils.java @@ -0,0 +1,278 @@ +package com.yfd.monitor.gdw2019.utils; + +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.Gb28181Sdp; +import com.yfd.monitor.gdw2019.bean.RemoteAddressInfo; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.GitUtil; +import gov.nist.javax.sip.address.AddressImpl; +import gov.nist.javax.sip.address.SipUri; +import gov.nist.javax.sip.message.SIPRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ObjectUtils; + +import javax.sdp.SdpFactory; +import javax.sdp.SdpParseException; +import javax.sdp.SessionDescription; +import javax.sip.PeerUnavailableException; +import javax.sip.SipFactory; +import javax.sip.header.FromHeader; +import javax.sip.header.ToHeader; +import javax.sip.header.UserAgentHeader; +import javax.sip.message.Request; +import java.text.ParseException; +import java.time.LocalDateTime; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @version 1.0.0 + * @description JAIN SIP的工具类 + * @createTime 2021年09月27日 15:12:00 + */ +public class SipUtils { + private final static Logger logger = LoggerFactory.getLogger(SipUtils.class); + + public static String getUserIdFromFromHeader(Request request) { + FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); + return getUserIdFromFromHeader(fromHeader); + } + /** + * 从subject读取channelId + * */ + public static String getChannelIdFromRequest(Request request) { + ToHeader tomHeader = (ToHeader)request.getHeader(ToHeader.NAME); + AddressImpl address = (AddressImpl)tomHeader.getAddress(); + SipUri uri = (SipUri) address.getURI(); + return uri.getUser(); + } + + public static String getUserIdFromFromHeader(FromHeader fromHeader) { + AddressImpl address = (AddressImpl)fromHeader.getAddress(); + SipUri uri = (SipUri) address.getURI(); + return uri.getUser(); + } + + public static String getNewViaTag() { + return "z9hG4bK" + System.currentTimeMillis(); + + } + + public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException { +// List agentParam = new ArrayList<>(); +// agentParam.add("MonitorServer "); +// if (gitUtil != null ) { +// if (!ObjectUtils.isEmpty(gitUtil.getBuildVersion())) { +// agentParam.add("v"); +// agentParam.add(gitUtil.getBuildVersion() + "."); +// } +// if (!ObjectUtils.isEmpty(gitUtil.getCommitTime())) { +// agentParam.add(gitUtil.getCommitTime()); +// } +// } + List agentParam = new ArrayList<>(); + agentParam.add("eXosip/5.0.0"); + return SipFactory.getInstance().createHeaderFactory().createUserAgentHeader(agentParam); + } + + public static String getNewFromTag(){ + return UUID.randomUUID().toString().replace("-", ""); + +// return getNewTag(); + } + + public static String getNewTag(){ + return String.valueOf(System.currentTimeMillis()); + } + + + /** + * 云台指令码计算 + * + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 默认 0XFF (0-255) + * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255) + */ + public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) { + int cmdCode = 0; + if (leftRight == 2) { + cmdCode|=0x01; // 右移 + } else if(leftRight == 1) { + cmdCode|=0x02; // 左移 + } + if (upDown == 2) { + cmdCode|=0x04; // 下移 + } else if(upDown == 1) { + cmdCode|=0x08; // 上移 + } + if (inOut == 2) { + cmdCode |= 0x10; // 放大 + } else if(inOut == 1) { + cmdCode |= 0x20; // 缩小 + } + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", moveSpeed); + builder.append(strTmp, 0, 2); + builder.append(strTmp, 0, 2); + strTmp = String.format("%X", zoomSpeed); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 从请求中获取设备ip地址和端口号 + * @param request 请求 + * @param sipUseSourceIpAsRemoteAddress false 从via中获取地址, true 直接获取远程地址 + * @return 地址信息 + */ + public static RemoteAddressInfo getRemoteAddressFromRequest(SIPRequest request, boolean sipUseSourceIpAsRemoteAddress) { + + String remoteAddress; + int remotePort; + if (sipUseSourceIpAsRemoteAddress) { + remoteAddress = request.getRemoteAddress().getHostAddress(); + remotePort = request.getRemotePort(); + }else { + // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息 + // 获取到通信地址等信息 + remoteAddress = request.getTopmostViaHeader().getReceived(); + remotePort = request.getTopmostViaHeader().getRPort(); + // 解析本地地址替代 + if (ObjectUtils.isEmpty(remoteAddress) || remotePort == -1) { + remoteAddress = request.getRemoteAddress().getHostAddress(); + remotePort = request.getRemotePort(); + } + } + + return new RemoteAddressInfo(remoteAddress, remotePort); + } + + public static DeviceChannel updateGps(DeviceChannel deviceChannel, String geoCoordSys) { + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { + + if (geoCoordSys == null) { + geoCoordSys = "WGS84"; + } + if ("WGS84".equals(geoCoordSys)) { + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeGcj02(position[0]); + deviceChannel.setLatitudeGcj02(position[1]); + }else if ("GCJ02".equals(geoCoordSys)) { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(position[0]); + deviceChannel.setLatitudeWgs84(position[1]); + }else { + deviceChannel.setLongitudeGcj02(0.00); + deviceChannel.setLatitudeGcj02(0.00); + deviceChannel.setLongitudeWgs84(0.00); + deviceChannel.setLatitudeWgs84(0.00); + } + }else { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + } + return deviceChannel; + } + + /** + * 判断是否是前端外围设备 + * @param deviceId + * @return + */ + public static boolean isFrontEnd(String deviceId) { + int typeCodeFromGbCode = getTypeCodeFromGbCode(deviceId); + return typeCodeFromGbCode > 130 && typeCodeFromGbCode < 199; + } + + public static Gb28181Sdp parseSDP(String sdpStr) throws SdpParseException { + + // jainSip不支持y= f=字段, 移除以解析。 + int ssrcIndex = sdpStr.indexOf("y="); + int mediaDescriptionIndex = sdpStr.indexOf("f="); + // 检查是否有y字段 + SessionDescription sdp; + String ssrc = null; + String mediaDescription = null; + if (mediaDescriptionIndex == 0 && ssrcIndex == 0) { + sdp = SdpFactory.getInstance().createSessionDescription(sdpStr); + }else { + String lines[] = sdpStr.split("\\r?\\n"); + StringBuilder sdpBuffer = new StringBuilder(); + for (String line : lines) { + if (line.trim().startsWith("y=")) { + ssrc = line.substring(2); + }else if (line.trim().startsWith("f=")) { + mediaDescription = line.substring(2); + }else { + sdpBuffer.append(line.trim()).append("\r\n"); + } + } + sdp = SdpFactory.getInstance().createSessionDescription(sdpBuffer.toString()); + } + return Gb28181Sdp.getInstance(sdp, ssrc, mediaDescription); + } + + public static String getSsrcFromSdp(String sdpStr) { + + // jainSip不支持y= f=字段, 移除以解析。 + int ssrcIndex = sdpStr.indexOf("y="); + if (ssrcIndex == 0) { + return null; + } + String lines[] = sdpStr.split("\\r?\\n"); + for (String line : lines) { + if (line.trim().startsWith("y=")) { + return line.substring(2); + } + } + return null; + } + + public static String parseTime(String timeStr) { + if (ObjectUtils.isEmpty(timeStr)){ + return null; + } + LocalDateTime localDateTime; + try { + localDateTime = LocalDateTime.parse(timeStr); + }catch (DateTimeParseException e) { + try { + localDateTime = LocalDateTime.parse(timeStr, DateUtil.formatterISO8601); + }catch (DateTimeParseException e2) { + logger.error("[格式化时间] 无法格式化时间: {}", timeStr); + return null; + } + } + return localDateTime.format(DateUtil.formatter); + } + + public static int getTypeCodeFromGbCode(String deviceId) { + if (ObjectUtils.isEmpty(deviceId)) { + return 0; + } + return Integer.parseInt(deviceId.substring(10, 13)); + } + + public static String getNewCallId() { + return (int) Math.floor(Math.random() * 1000000000) + ""; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/StreamPusher.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/StreamPusher.java new file mode 100644 index 0000000..4d81363 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/StreamPusher.java @@ -0,0 +1,95 @@ +package com.yfd.monitor.gdw2019.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +@Slf4j +@Component +public class StreamPusher { + private Process process; + private Thread outputThread; + private Thread errorThread; + + public boolean pushStreamToParent(String ffmpeg_path,String fromRtsp, String parentRtsp) { + String[] command = { + ffmpeg_path + "ffmpeg", + "-rtsp_transport", "tcp", + "-i", fromRtsp, + "-c:v", "copy", + "-c:a", "copy", + "-f", "rtsp", + parentRtsp + }; + try { + process = new ProcessBuilder(command).start(); + + outputThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + log.info(line); + } + } catch (IOException e) { + log.error("发生错误", e); + } + }); + + errorThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = reader.readLine()) != null) { + log.info(line); + } + } catch (IOException e) { + log.error("发生错误", e); + } + }); + + outputThread.start(); + errorThread.start(); + + // 不再等待线程完成 + } catch (IOException e) { + log.error("发生异常", e); + return false; + } + return true; + } + + // 新增停止推流的方法 + public void stopStreamProcess() { + if (process != null) { + process.destroy(); + } + + // 在这里中断两个线程 + if (outputThread != null) { + outputThread.interrupt(); + } + if (errorThread != null) { + errorThread.interrupt(); + } + } + + public static void main(String[] args) { + StreamPusher streamPusher = new StreamPusher(); + String FFMPEG_PATH = "D:\\ZLMediaKit_Win\\ffmpeg\\bin\\ffmpeg"; + String FROM_RTSP = "rtsp://192.168.1.13:554/rtp/34020000001320000001_34020000001320000009"; + String PARENT_RTSP = "rtsp://192.168.1.13:554/live/test04?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc"; + boolean success = streamPusher.pushStreamToParent(FFMPEG_PATH, FROM_RTSP,PARENT_RTSP); + + if (success) { + log.info("推流成功!"); + } else { + log.info("推流失败!"); + } + // 在需要停止推流的时候调用 stopStreamProcess 方法 + streamPusher.stopStreamProcess(); + } +} + + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/VideoToImageUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/VideoToImageUtil.java new file mode 100644 index 0000000..6c25deb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/VideoToImageUtil.java @@ -0,0 +1,81 @@ +package com.yfd.monitor.gdw2019.utils; + +import cn.hutool.core.io.FileUtil; +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +@Slf4j +public class VideoToImageUtil { + + public static void getVideoPicture(String ffmpegpath,String videoUrl, String imgfile) { + try { + System.out.println("ffmpegpath"+ffmpegpath); + System.out.println("videoUrl"+videoUrl); + System.out.println("imgfile"+imgfile); + FileUtil.touch(imgfile); + String[] command = { + ffmpegpath + "ffmpeg", + "-rtsp_transport", "tcp", + "-i", videoUrl, + "-f", "mjpeg", + "-t", "0.001", + "-y", // 强制覆盖已存在的文件 + imgfile + }; + Process process = new ProcessBuilder(command).start(); + // 创建并启动线程处理标准输出流 + Thread outputThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + System.err.println(line); + } + } catch (IOException e) { + log.error("发生错误", e); + throw new RuntimeException(e.getMessage()); + } + }); + // 启动线程 + outputThread.start(); + Thread errorThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = reader.readLine()) != null) { + } + } catch (IOException e) { + log.error("发生错误", e); + throw new RuntimeException(e.getMessage()); + } + }); + errorThread.start(); + // 等待FFmpeg进程完成 + int exitCode = process.waitFor(); + if (exitCode == 0) { + log.info("视频帧提取完毕!"); + } else { + log.info("FFmpeg进程出错"); + } + } catch (Exception e) { + log.error("发生错误", e); + throw new RuntimeException("抓取视频失败"); + } + } + + public static void main(String[] args) { + try { + String ffmpegpath = "d:\\ZLMediaKit_Win\\ffmpeg\\bin\\"; + //String videoUrl = "rtsp://admin:JY123456@192.168.1.66:554/Streaming/Channels/101"; + String videoUrl = "rtsp://192.168.1.13:554/rtp/34020000001110000009_34020000001310000001"; + String audioPath = "D:/test.wav"; + VideoToImageUtil.getVideoPicture(ffmpegpath,videoUrl,audioPath); + } catch (Exception e) { + log.error("发生错误", e); + throw new RuntimeException(e.getMessage()); + } + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/XmlUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/XmlUtil.java new file mode 100644 index 0000000..d26df31 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/gdw2019/utils/XmlUtil.java @@ -0,0 +1,491 @@ +package com.yfd.monitor.gdw2019.utils; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.utils.DateUtil; +import org.dom4j.Attribute; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; + +import javax.sip.RequestEvent; +import javax.sip.message.Request; +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; + +/** + * 基于dom4j的工具包 + * + * + */ +public class XmlUtil { + /** + * 日志服务 + */ + private static Logger logger = LoggerFactory.getLogger(XmlUtil.class); + + /** + * 解析XML为Document对象 + * + * @param xml 被解析的XMl + * + * @return Document + */ + public static Element parseXml(String xml) { + Document document = null; + // + StringReader sr = new StringReader(xml); + SAXReader saxReader = new SAXReader(); + try { + document = saxReader.read(sr); + } catch (DocumentException e) { + logger.error("解析失败", e); + } + return null == document ? null : document.getRootElement(); + } + + /** + * 获取element对象的text的值 + * + * @param em 节点的对象 + * @param tag 节点的tag + * @return 节点 + */ + public static String getText(Element em, String tag) { + if (null == em) { + return null; + } + Element e = em.element(tag); + // + return null == e ? null : e.getText().trim(); + } + + /** + * 递归解析xml节点,适用于 多节点数据 + * + * @param node node + * @param nodeName nodeName + * @return List> + */ + public static List> listNodes(Element node, String nodeName) { + if (null == node) { + return null; + } + // 初始化返回 + List> listMap = new ArrayList>(); + // 首先获取当前节点的所有属性节点 + List list = node.attributes(); + + Map map = null; + // 遍历属性节点 + for (Attribute attribute : list) { + if (nodeName.equals(node.getName())) { + if (null == map) { + map = new HashMap(); + listMap.add(map); + } + // 取到的节点属性放到map中 + map.put(attribute.getName(), attribute.getValue()); + } + + } + // 遍历当前节点下的所有节点 ,nodeName 要解析的节点名称 + // 使用递归 + Iterator iterator = node.elementIterator(); + while (iterator.hasNext()) { + Element e = iterator.next(); + listMap.addAll(listNodes(e, nodeName)); + } + return listMap; + } + + /** + * xml转json + * + * @param element + * @param json + */ + public static void node2Json(Element element, JSONObject json) { + // 如果是属性 + for (Object o : element.attributes()) { + Attribute attr = (Attribute) o; + if (!ObjectUtils.isEmpty(attr.getValue())) { + json.put("@" + attr.getName(), attr.getValue()); + } + } + List chdEl = element.elements(); + if (chdEl.isEmpty() && !ObjectUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值 + json.put(element.getName(), element.getText()); + } + + for (Element e : chdEl) { // 有子元素 + if (!e.elements().isEmpty()) { // 子元素也有子元素 + JSONObject chdjson = new JSONObject(); + node2Json(e, chdjson); + Object o = json.get(e.getName()); + if (o != null) { + JSONArray jsona = null; + if (o instanceof JSONObject) { // 如果此元素已存在,则转为jsonArray + JSONObject jsono = (JSONObject) o; + json.remove(e.getName()); + jsona = new JSONArray(); + jsona.add(jsono); + jsona.add(chdjson); + } + if (o instanceof JSONArray) { + jsona = (JSONArray) o; + jsona.add(chdjson); + } + json.put(e.getName(), jsona); + } else { + if (!chdjson.isEmpty()) { + json.put(e.getName(), chdjson); + } + } + } else { // 子元素没有子元素 + for (Object o : element.attributes()) { + Attribute attr = (Attribute) o; + if (!ObjectUtils.isEmpty(attr.getValue())) { + json.put("@" + attr.getName(), attr.getValue()); + } + } + if (!e.getText().isEmpty()) { + json.put(e.getName(), e.getText()); + } + } + } + } + public static Element getRootElement(RequestEvent evt) throws DocumentException { + + return getRootElement(evt, "gb2312"); + } + + public static Element getRootElement(RequestEvent evt, String charset) throws DocumentException { + Request request = evt.getRequest(); + return getRootElement(request.getRawContent(), charset); + } + + public static Element getRootElement(byte[] content, String charset) throws DocumentException { + if (charset == null) { + charset = "gb2312"; + } + SAXReader reader = new SAXReader(); + reader.setEncoding(charset); + Document xml = reader.read(new ByteArrayInputStream(content)); + return xml.getRootElement(); + } + + private enum ChannelType{ + CivilCode, BusinessGroup,VirtualOrganization,Other + } + + public static DeviceChannel channelContentHander(Element itemDevice, Device device, String event){ + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setDeviceId(device.getDeviceId()); + Element channdelIdElement = itemDevice.element("DeviceID"); + if (channdelIdElement == null) { + logger.warn("解析Catalog消息时发现缺少 DeviceID"); + return null; + } + String channelId = channdelIdElement.getTextTrim(); + if (ObjectUtils.isEmpty(channelId)) { + logger.warn("解析Catalog消息时发现缺少 DeviceID"); + return null; + } + deviceChannel.setChannelId(channelId); + if (event != null && !event.equals(CatalogEvent.ADD) && !event.equals(CatalogEvent.UPDATE)) { + // 除了ADD和update情况下需要识别全部内容, + return deviceChannel; + } + + ChannelType channelType = ChannelType.Other; + if (channelId.length() <= 8) { + channelType = ChannelType.CivilCode; + deviceChannel.setHasAudio(false); + }else { + if (channelId.length() == 20) { + int code = Integer.parseInt(channelId.substring(10, 13)); + switch (code){ + case 215: + channelType = ChannelType.BusinessGroup; + deviceChannel.setHasAudio(false); + break; + case 216: + channelType = ChannelType.VirtualOrganization; + deviceChannel.setHasAudio(false); + break; + case 136: + case 137: + case 138: + deviceChannel.setHasAudio(true); + break; + default: + deviceChannel.setHasAudio(false); + break; + + } + } + } + + Element channdelNameElement = itemDevice.element("Name"); + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim() : ""; + deviceChannel.setName(channelName); + + String civilCode = XmlUtil.getText(itemDevice, "CivilCode"); + deviceChannel.setCivilCode(civilCode); + if (channelType == ChannelType.CivilCode && civilCode == null) { + deviceChannel.setParental(1); + // 行政区划如果没有传递具体值,则推测一个 + if (channelId.length() > 2) { + deviceChannel.setCivilCode(channelId.substring(0, channelId.length() - 2)); + } + } + if (channelType.equals(ChannelType.CivilCode)) { + // 行政区划其他字段没必要识别了,默认在线即可 + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + return deviceChannel; + } + /** + * 行政区划展示设备树与业务分组展示设备树是两种不同的模式 + * 行政区划展示设备树 各个目录之间主要靠deviceId做关联,摄像头通过CivilCode指定其属于那个行政区划;都是不超过十位的编号; 结构如下: + * 河北省 + * --> 石家庄市 + * --> 摄像头 + *String parentId = XmlUtil.getText(itemDevice, "ParentID"); + if (parentId != null) { + if (parentId.contains("/")) { + String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1); + String businessGroup = parentId.substring(0, parentId.indexOf("/")); + deviceChannel.setParentId(lastParentId); + }else { + deviceChannel.setParentId(parentId); + } + } + deviceCh --> 正定县 + * --> 摄像头 + * --> 摄像头 + * + * 业务分组展示设备树是顶级是业务分组,其下的虚拟组织靠BusinessGroupID指定其所属的业务分组;摄像头通过ParentId来指定其所属于的虚拟组织: + * 业务分组 + * --> 虚拟组织 + * --> 摄像头 + * --> 虚拟组织 + * --> 摄像头 + * --> 摄像头 + */ + String parentId = XmlUtil.getText(itemDevice, "ParentID"); + String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID"); + if (parentId != null) { + if (parentId.contains("/")) { + String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1); + if (businessGroupID == null) { + businessGroupID = parentId.substring(0, parentId.indexOf("/")); + } + deviceChannel.setParentId(lastParentId); + }else { + deviceChannel.setParentId(parentId); + } + // 兼容设备通道信息中自己为自己父节点的情况 + if (deviceChannel.getParentId().equals(deviceChannel.getChannelId())) { + deviceChannel.setParentId(null); + } + } + deviceChannel.setBusinessGroupId(businessGroupID); + if (channelType.equals(ChannelType.BusinessGroup) || channelType.equals(ChannelType.VirtualOrganization)) { + // 业务分组和虚拟组织 其他字段没必要识别了,默认在线即可 + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + return deviceChannel; + } + + Element statusElement = itemDevice.element("Status"); + + if (statusElement != null) { + String status = statusElement.getTextTrim().trim(); + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) { + deviceChannel.setStatus(1); + } + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { + deviceChannel.setStatus(0); + } + }else { + deviceChannel.setStatus(1); + } + // 识别自带的目录标识 + String parental = XmlUtil.getText(itemDevice, "Parental"); + // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 + if (!ObjectUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) { + deviceChannel.setParental(0); + }else { + deviceChannel.setParental(1); + } + + + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); + + String safetyWay = XmlUtil.getText(itemDevice, "SafetyWay"); + if (ObjectUtils.isEmpty(safetyWay)) { + deviceChannel.setSafetyWay(0); + } else { + deviceChannel.setSafetyWay(Integer.parseInt(safetyWay)); + } + + String registerWay = XmlUtil.getText(itemDevice, "RegisterWay"); + if (ObjectUtils.isEmpty(registerWay)) { + deviceChannel.setRegisterWay(1); + } else { + deviceChannel.setRegisterWay(Integer.parseInt(registerWay)); + } + + if (XmlUtil.getText(itemDevice, "Certifiable") == null + || XmlUtil.getText(itemDevice, "Certifiable") == "") { + deviceChannel.setCertifiable(0); + } else { + deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable"))); + } + + if (XmlUtil.getText(itemDevice, "ErrCode") == null + || XmlUtil.getText(itemDevice, "ErrCode") == "") { + deviceChannel.setErrCode(0); + } else { + deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode"))); + } + + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime")); + deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy")); + deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress")); + if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") { + deviceChannel.setPort(0); + } else { + deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port"))); + } + + + String longitude = XmlUtil.getText(itemDevice, "Longitude"); + if (NumericUtil.isDouble(longitude)) { + deviceChannel.setLongitude(Double.parseDouble(longitude)); + } else { + deviceChannel.setLongitude(0.00); + } + String latitude = XmlUtil.getText(itemDevice, "Latitude"); + if (NumericUtil.isDouble(latitude)) { + deviceChannel.setLatitude(Double.parseDouble(latitude)); + } else { + deviceChannel.setLatitude(0.00); + } + + deviceChannel.setGpsTime(DateUtil.getNow()); + + + if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) { + //兼容INFO中的信息 + Element info = itemDevice.element("Info"); + if(XmlUtil.getText(info, "PTZType") == null || "".equals(XmlUtil.getText(info, "PTZType"))){ + deviceChannel.setPTZType(0); + }else{ + deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(info, "PTZType"))); + } + } else { + deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); + } + + return deviceChannel; + } + + /** + * 新增方法支持内部嵌套 + * + * @param element xmlElement + * @param clazz 结果类 + * @param 泛型 + * @return 结果对象 + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws InstantiationException + * @throws IllegalAccessException + */ + public static T loadElement(Element element, Class clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Field[] fields = clazz.getDeclaredFields(); + T t = clazz.getDeclaredConstructor().newInstance(); + for (Field field : fields) { + ReflectionUtils.makeAccessible(field); + MessageElement annotation = field.getAnnotation(MessageElement.class); + if (annotation == null) { + continue; + } + String value = annotation.value(); + String subVal = annotation.subVal(); + Element element1 = element.element(value); + if (element1 == null) { + continue; + } + if ("".equals(subVal)) { + // 无下级数据 + Object fieldVal = element1.isTextOnly() ? element1.getText() : loadElement(element1, field.getType()); + Object o = simpleTypeDeal(field.getType(), fieldVal); + ReflectionUtils.setField(field, t, o); + } else { + // 存在下级数据 + ArrayList list = new ArrayList<>(); + Type genericType = field.getGenericType(); + if (!(genericType instanceof ParameterizedType)) { + continue; + } + Class aClass = (Class) ((ParameterizedType) genericType).getActualTypeArguments()[0]; + for (Element element2 : element1.elements(subVal)) { + list.add(loadElement(element2, aClass)); + } + ReflectionUtils.setField(field, t, list); + } + } + return t; + } + + /** + * 简单类型处理 + * + * @param tClass + * @param val + * @return + */ + private static Object simpleTypeDeal(Class tClass, Object val) { + if (tClass.equals(String.class)) { + return val.toString(); + } + if (tClass.equals(Integer.class)) { + return Integer.valueOf(val.toString()); + } + if (tClass.equals(Double.class)) { + return Double.valueOf(val.toString()); + } + if (tClass.equals(Long.class)) { + return Long.valueOf(val.toString()); + } + return val; + } +} \ No newline at end of file diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/bean/MediaInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/media/bean/MediaInfo.java new file mode 100644 index 0000000..86fe64a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/bean/MediaInfo.java @@ -0,0 +1,318 @@ +package com.yfd.monitor.media.bean; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +/** + * 视频信息 + */ +@Schema(description = "视频信息") +public class MediaInfo { + @Schema(description = "应用名") + private String app; + @Schema(description = "流ID") + private String stream; + @Schema(description = "流媒体节点") + private MediaServerItem mediaServer; + @Schema(description = "协议") + private String schema; + + @Schema(description = "观看人数") + private Integer readerCount; + @Schema(description = "视频编码类型") + private String videoCodec; + @Schema(description = "视频宽度") + private Integer width; + @Schema(description = "视频高度") + private Integer height; + @Schema(description = "音频编码类型") + private String audioCodec; + @Schema(description = "音频通道数") + private Integer audioChannels; + @Schema(description = "音频采样率") + private Integer audioSampleRate; + @Schema(description = "音频采样率") + private Long duration; + @Schema(description = "在线") + private Boolean online; + @Schema(description = "unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7") + private Integer originType; + @Schema(description = "存活时间,单位秒") + private Long aliveSecond; + @Schema(description = "数据产生速度,单位byte/s") + private Long bytesSpeed; + @Schema(description = "鉴权参数") + private String callId; + + public static MediaInfo getInstance(JSONObject jsonObject, MediaServerItem mediaServer) { + MediaInfo mediaInfo = new MediaInfo(); + mediaInfo.setMediaServer(mediaServer); + String app = jsonObject.getString("app"); + mediaInfo.setApp(app); + String stream = jsonObject.getString("stream"); + mediaInfo.setStream(stream); + String schema = jsonObject.getString("schema"); + mediaInfo.setSchema(schema); + Integer totalReaderCount = jsonObject.getInteger("totalReaderCount"); + Boolean online = jsonObject.getBoolean("online"); + Integer originType = jsonObject.getInteger("originType"); + Long aliveSecond = jsonObject.getLong("aliveSecond"); + Long bytesSpeed = jsonObject.getLong("bytesSpeed"); + if (totalReaderCount != null) { + mediaInfo.setReaderCount(totalReaderCount); + } + if (online != null) { + mediaInfo.setOnline(online); + } + if (originType != null) { + mediaInfo.setOriginType(originType); + } + if (aliveSecond != null) { + mediaInfo.setAliveSecond(aliveSecond); + } + if (bytesSpeed != null) { + mediaInfo.setBytesSpeed(bytesSpeed); + } + JSONArray jsonArray = jsonObject.getJSONArray("tracks"); + if (jsonArray.isEmpty()) { + return null; + } + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject trackJson = jsonArray.getJSONObject(i); + Integer channels = trackJson.getInteger("channels"); + Integer codecId = trackJson.getInteger("codec_id"); + Integer codecType = trackJson.getInteger("codec_type"); + Integer sampleRate = trackJson.getInteger("sample_rate"); + Integer height = trackJson.getInteger("height"); + Integer width = trackJson.getInteger("height"); + Long duration = trackJson.getLongValue("duration"); + if (channels != null) { + mediaInfo.setAudioChannels(channels); + } + if (sampleRate != null) { + mediaInfo.setAudioSampleRate(sampleRate); + } + if (height != null) { + mediaInfo.setHeight(height); + } + if (width != null) { + mediaInfo.setWidth(width); + } + if (duration > 0L) { + mediaInfo.setDuration(duration); + } + if (codecId != null) { + switch (codecId) { + case 0: + mediaInfo.setVideoCodec("H264"); + break; + case 1: + mediaInfo.setVideoCodec("H265"); + break; + case 2: + mediaInfo.setAudioCodec("AAC"); + break; + case 3: + mediaInfo.setAudioCodec("G711A"); + break; + case 4: + mediaInfo.setAudioCodec("G711U"); + break; + } + } + } + return mediaInfo; + } + + public static MediaInfo getInstance(OnStreamChangedHookParam param, MediaServerItem mediaServer) { + + MediaInfo mediaInfo = new MediaInfo(); + mediaInfo.setApp(param.getApp()); + mediaInfo.setStream(param.getStream()); + mediaInfo.setSchema(param.getSchema()); + mediaInfo.setMediaServer(mediaServer); + mediaInfo.setReaderCount(Integer.parseInt(param.getTotalReaderCount())); + mediaInfo.setOnline(param.isRegist()); + mediaInfo.setOriginType(param.getOriginType()); + mediaInfo.setAliveSecond(param.getAliveSecond()); + mediaInfo.setBytesSpeed(param.getBytesSpeed()); + List tracks = param.getTracks(); + if (tracks == null || tracks.isEmpty()) { + return mediaInfo; + } + for (OnStreamChangedHookParam.MediaTrack mediaTrack : tracks) { + switch (mediaTrack.getCodecId()) { + case 0: + mediaInfo.setVideoCodec("H264"); + break; + case 1: + mediaInfo.setVideoCodec("H265"); + break; + case 2: + mediaInfo.setAudioCodec("AAC"); + break; + case 3: + mediaInfo.setAudioCodec("G711A"); + break; + case 4: + mediaInfo.setAudioCodec("G711U"); + break; + } + if (mediaTrack.getSampleRate() > 0) { + mediaInfo.setAudioSampleRate(mediaTrack.getSampleRate()); + } + if (mediaTrack.getChannels() > 0) { + mediaInfo.setAudioChannels(mediaTrack.getChannels()); + } + if (mediaTrack.getHeight() > 0) { + mediaInfo.setHeight(mediaTrack.getHeight()); + } + if (mediaTrack.getWidth() > 0) { + mediaInfo.setWidth(mediaTrack.getWidth()); + } + } + return mediaInfo; + } + + public Integer getReaderCount() { + return readerCount; + } + + public void setReaderCount(Integer readerCount) { + this.readerCount = readerCount; + } + + public String getVideoCodec() { + return videoCodec; + } + + public void setVideoCodec(String videoCodec) { + this.videoCodec = videoCodec; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } + + public String getAudioCodec() { + return audioCodec; + } + + public void setAudioCodec(String audioCodec) { + this.audioCodec = audioCodec; + } + + public Integer getAudioChannels() { + return audioChannels; + } + + public void setAudioChannels(Integer audioChannels) { + this.audioChannels = audioChannels; + } + + public Integer getAudioSampleRate() { + return audioSampleRate; + } + + public void setAudioSampleRate(Integer audioSampleRate) { + this.audioSampleRate = audioSampleRate; + } + + public Long getDuration() { + return duration; + } + + public void setDuration(Long duration) { + this.duration = duration; + } + + public Boolean getOnline() { + return online; + } + + public void setOnline(Boolean online) { + this.online = online; + } + + public Integer getOriginType() { + return originType; + } + + public void setOriginType(Integer originType) { + this.originType = originType; + } + + public Long getAliveSecond() { + return aliveSecond; + } + + public void setAliveSecond(Long aliveSecond) { + this.aliveSecond = aliveSecond; + } + + public Long getBytesSpeed() { + return bytesSpeed; + } + + public void setBytesSpeed(Long bytesSpeed) { + this.bytesSpeed = bytesSpeed; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public MediaServerItem getMediaServer() { + return mediaServer; + } + + public void setMediaServer(MediaServerItem mediaServer) { + this.mediaServer = mediaServer; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/bean/MediaServer.java b/riis-monitor/src/main/java/com/yfd/monitor/media/bean/MediaServer.java new file mode 100644 index 0000000..ef7e556 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/bean/MediaServer.java @@ -0,0 +1,391 @@ +package com.yfd.monitor.media.bean; + + +import com.yfd.monitor.media.zlm.ZLMServerConfig; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.ObjectUtils; + +@Schema(description = "流媒体服务信息") +public class MediaServer { + + @Schema(description = "ID") + private String id; + + @Schema(description = "IP") + private String ip; + + @Schema(description = "hook使用的IP(zlm访问WVP使用的IP)") + private String hookIp; + + @Schema(description = "SDP IP") + private String sdpIp; + + @Schema(description = "流IP") + private String streamIp; + + @Schema(description = "HTTP端口") + private int httpPort; + + @Schema(description = "HTTPS端口") + private int httpSSlPort; + + @Schema(description = "RTMP端口") + private int rtmpPort; + + @Schema(description = "flv端口") + private int flvPort; + + @Schema(description = "https-flv端口") + private int flvSSLPort; + + @Schema(description = "ws-flv端口") + private int wsFlvPort; + + @Schema(description = "wss-flv端口") + private int wsFlvSSLPort; + + @Schema(description = "RTMPS端口") + private int rtmpSSlPort; + + @Schema(description = "RTP收流端口(单端口模式有用)") + private int rtpProxyPort; + + @Schema(description = "RTSP端口") + private int rtspPort; + + @Schema(description = "RTSPS端口") + private int rtspSSLPort; + + @Schema(description = "是否开启自动配置ZLM") + private boolean autoConfig; + + @Schema(description = "ZLM鉴权参数") + private String secret; + + @Schema(description = "keepalive hook触发间隔,单位秒") + private Float hookAliveInterval; + + @Schema(description = "是否使用多端口模式") + private boolean rtpEnable; + + @Schema(description = "状态") + private boolean status; + + @Schema(description = "多端口RTP收流端口范围") + private String rtpPortRange; + + @Schema(description = "RTP发流端口范围") + private String sendRtpPortRange; + + @Schema(description = "assist服务端口") + private int recordAssistPort; + + @Schema(description = "创建时间") + private String createTime; + + @Schema(description = "更新时间") + private String updateTime; + + @Schema(description = "上次心跳时间") + private String lastKeepaliveTime; + + @Schema(description = "是否是默认ZLM") + private boolean defaultServer; + + @Schema(description = "录像存储时长") + private int recordDay; + + @Schema(description = "录像存储路径") + private String recordPath; + @Schema(description = "类型: zlm/abl") + private String type; + + @Schema(description = "转码的前缀") + private String transcodeSuffix; + + public MediaServer() { + } + + public MediaServer(ZLMServerConfig zlmServerConfig, String sipIp) { + id = zlmServerConfig.getGeneralMediaServerId(); + ip = zlmServerConfig.getIp(); + hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp(); + sdpIp = ObjectUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp(); + streamIp = ObjectUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp(); + httpPort = zlmServerConfig.getHttpPort(); + flvPort = zlmServerConfig.getHttpPort(); + wsFlvPort = zlmServerConfig.getHttpPort(); + httpSSlPort = zlmServerConfig.getHttpSSLport(); + flvSSLPort = zlmServerConfig.getHttpSSLport(); + wsFlvSSLPort = zlmServerConfig.getHttpSSLport(); + rtmpPort = zlmServerConfig.getRtmpPort(); + rtmpSSlPort = zlmServerConfig.getRtmpSslPort(); + rtpProxyPort = zlmServerConfig.getRtpProxyPort(); + rtspPort = zlmServerConfig.getRtspPort(); + rtspSSLPort = zlmServerConfig.getRtspSSlport(); + autoConfig = true; // 默认值true; + secret = zlmServerConfig.getApiSecret(); + hookAliveInterval = zlmServerConfig.getHookAliveInterval(); + rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 + rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 + recordAssistPort = 0; // 默认关闭 + transcodeSuffix = zlmServerConfig.getTranscodeSuffix(); + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getStreamIp() { + return streamIp; + } + + public void setStreamIp(String streamIp) { + this.streamIp = streamIp; + } + + public int getHttpPort() { + return httpPort; + } + + public void setHttpPort(int httpPort) { + this.httpPort = httpPort; + } + + public int getHttpSSlPort() { + return httpSSlPort; + } + + public void setHttpSSlPort(int httpSSlPort) { + this.httpSSlPort = httpSSlPort; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public void setRtmpPort(int rtmpPort) { + this.rtmpPort = rtmpPort; + } + + public int getRtmpSSlPort() { + return rtmpSSlPort; + } + + public void setRtmpSSlPort(int rtmpSSlPort) { + this.rtmpSSlPort = rtmpSSlPort; + } + + public int getRtpProxyPort() { + return rtpProxyPort; + } + + public void setRtpProxyPort(int rtpProxyPort) { + this.rtpProxyPort = rtpProxyPort; + } + + public int getRtspPort() { + return rtspPort; + } + + public void setRtspPort(int rtspPort) { + this.rtspPort = rtspPort; + } + + public int getRtspSSLPort() { + return rtspSSLPort; + } + + public void setRtspSSLPort(int rtspSSLPort) { + this.rtspSSLPort = rtspSSLPort; + } + + public boolean isAutoConfig() { + return autoConfig; + } + + public void setAutoConfig(boolean autoConfig) { + this.autoConfig = autoConfig; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public boolean isRtpEnable() { + return rtpEnable; + } + + public void setRtpEnable(boolean rtpEnable) { + this.rtpEnable = rtpEnable; + } + + public String getRtpPortRange() { + return rtpPortRange; + } + + public void setRtpPortRange(String rtpPortRange) { + this.rtpPortRange = rtpPortRange; + } + + public int getRecordAssistPort() { + return recordAssistPort; + } + + public void setRecordAssistPort(int recordAssistPort) { + this.recordAssistPort = recordAssistPort; + } + + public boolean isDefaultServer() { + return defaultServer; + } + + public void setDefaultServer(boolean defaultServer) { + this.defaultServer = defaultServer; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public String getLastKeepaliveTime() { + return lastKeepaliveTime; + } + + public void setLastKeepaliveTime(String lastKeepaliveTime) { + this.lastKeepaliveTime = lastKeepaliveTime; + } + + public Float getHookAliveInterval() { + return hookAliveInterval; + } + + public void setHookAliveInterval(Float hookAliveInterval) { + this.hookAliveInterval = hookAliveInterval; + } + + public String getSendRtpPortRange() { + return sendRtpPortRange; + } + + public void setSendRtpPortRange(String sendRtpPortRange) { + this.sendRtpPortRange = sendRtpPortRange; + } + + public int getRecordDay() { + return recordDay; + } + + public void setRecordDay(int recordDay) { + this.recordDay = recordDay; + } + + public String getRecordPath() { + return recordPath; + } + + public void setRecordPath(String recordPath) { + this.recordPath = recordPath; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public int getFlvPort() { + return flvPort; + } + + public void setFlvPort(int flvPort) { + this.flvPort = flvPort; + } + + public int getFlvSSLPort() { + return flvSSLPort; + } + + public void setFlvSSLPort(int flvSSLPort) { + this.flvSSLPort = flvSSLPort; + } + + public int getWsFlvPort() { + return wsFlvPort; + } + + public void setWsFlvPort(int wsFlvPort) { + this.wsFlvPort = wsFlvPort; + } + + public int getWsFlvSSLPort() { + return wsFlvSSLPort; + } + + public void setWsFlvSSLPort(int wsFlvSSLPort) { + this.wsFlvSSLPort = wsFlvSSLPort; + } + + public String getTranscodeSuffix() { + return transcodeSuffix; + } + + public void setTranscodeSuffix(String transcodeSuffix) { + this.transcodeSuffix = transcodeSuffix; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/bean/RecordInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/media/bean/RecordInfo.java new file mode 100644 index 0000000..4598e4c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/bean/RecordInfo.java @@ -0,0 +1,102 @@ +package com.yfd.monitor.media.bean; + +import com.yfd.monitor.media.zlm.dto.hook.OnRecordMp4HookParam; + +public class RecordInfo { + private String fileName; + private String filePath; + private long fileSize; + private String folder; + private String url; + private long startTime; + private double timeLen; + private String params; + + public static RecordInfo getInstance(OnRecordMp4HookParam hookParam) { + RecordInfo recordInfo = new RecordInfo(); + recordInfo.setFileName(hookParam.getFile_name()); + recordInfo.setUrl(hookParam.getUrl()); + recordInfo.setFolder(hookParam.getFolder()); + recordInfo.setFilePath(hookParam.getFile_path()); + recordInfo.setFileSize(hookParam.getFile_size()); + recordInfo.setStartTime(hookParam.getStart_time()); + recordInfo.setTimeLen(hookParam.getTime_len()); + return recordInfo; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public long getFileSize() { + return fileSize; + } + + public void setFileSize(long fileSize) { + this.fileSize = fileSize; + } + + public String getFolder() { + return folder; + } + + public void setFolder(String folder) { + this.folder = folder; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public double getTimeLen() { + return timeLen; + } + + public void setTimeLen(double timeLen) { + this.timeLen = timeLen; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + @Override + public String toString() { + return "RecordInfo{" + + "文件名称='" + fileName + '\'' + + ", 文件路径='" + filePath + '\'' + + ", 文件大小=" + fileSize + + ", 开始时间=" + startTime + + ", 时长=" + timeLen + + ", params=" + params + + '}'; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/Hook.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/Hook.java new file mode 100644 index 0000000..83f7649 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/Hook.java @@ -0,0 +1,88 @@ +package com.yfd.monitor.media.event.hook; + +/** + * zlm hook事件的参数 + * @author lin + */ +public class Hook { + + private HookType hookType; + + private String app; + + private String stream; + + private String mediaServerId; + + private Long expireTime; + + + public static Hook getInstance(HookType hookType, String app, String stream, String mediaServerId) { + Hook hookSubscribe = new Hook(); + hookSubscribe.setApp(app); + hookSubscribe.setStream(stream); + hookSubscribe.setHookType(hookType); + hookSubscribe.setMediaServerId(mediaServerId); + hookSubscribe.setExpireTime(System.currentTimeMillis() + 5 * 60 * 1000); + return hookSubscribe; + } + + public HookType getHookType() { + return hookType; + } + + public void setHookType(HookType hookType) { + this.hookType = hookType; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + + public Long getExpireTime() { + return expireTime; + } + + public void setExpireTime(Long expireTime) { + this.expireTime = expireTime; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Hook) { + Hook param = (Hook) obj; + return param.getHookType().equals(this.hookType) + && param.getApp().equals(this.app) + && param.getStream().equals(this.stream) + && param.getMediaServerId().equals(this.mediaServerId); + }else { + return false; + } + } + + @Override + public String toString() { + return this.getHookType() + this.getApp() + this.getStream() + this.getMediaServerId(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookData.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookData.java new file mode 100644 index 0000000..6b20b48 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookData.java @@ -0,0 +1,133 @@ +package com.yfd.monitor.media.event.hook; + + +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.bean.RecordInfo; +import com.yfd.monitor.media.event.media.MediaArrivalEvent; +import com.yfd.monitor.media.event.media.MediaEvent; +import com.yfd.monitor.media.event.media.MediaPublishEvent; +import com.yfd.monitor.media.event.media.MediaRecordMp4Event; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * Hook返回的内容 + */ +public class HookData { + /** + * 应用名 + */ + private String app; + /** + * 流ID + */ + private String stream; + /** + * 流媒体节点 + */ + private MediaServerItem mediaServer; + /** + * 协议 + */ + private String schema; + + /** + * 流信息 + */ + private MediaInfo mediaInfo; + + /** + * 录像信息 + */ + private RecordInfo recordInfo; + + @Schema(description = "推流的额外参数") + private String params; + public static HookData getInstance(MediaEvent mediaEvent) { + HookData hookData = new HookData(); + if (mediaEvent instanceof MediaPublishEvent) { + MediaPublishEvent event = (MediaPublishEvent) mediaEvent; + hookData.setApp(event.getApp()); + hookData.setStream(event.getStream()); + hookData.setSchema(event.getSchema()); + hookData.setMediaServer(event.getMediaServer()); + hookData.setParams(event.getParams()); + }else if (mediaEvent instanceof MediaArrivalEvent) { + MediaArrivalEvent event = (MediaArrivalEvent) mediaEvent; + hookData.setApp(event.getApp()); + hookData.setStream(event.getStream()); + hookData.setSchema(event.getSchema()); + hookData.setMediaServer(event.getMediaServer()); + hookData.setMediaInfo(event.getMediaInfo()); + }else if (mediaEvent instanceof MediaRecordMp4Event) { + MediaRecordMp4Event event = (MediaRecordMp4Event) mediaEvent; + hookData.setApp(event.getApp()); + hookData.setStream(event.getStream()); + hookData.setSchema(event.getSchema()); + hookData.setMediaServer(event.getMediaServer()); + hookData.setRecordInfo(event.getRecordInfo()); + }else { + hookData.setApp(mediaEvent.getApp()); + hookData.setStream(mediaEvent.getStream()); + hookData.setSchema(mediaEvent.getSchema()); + hookData.setMediaServer(mediaEvent.getMediaServer()); + } + return hookData; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public MediaServerItem getMediaServer() { + return mediaServer; + } + + public void setMediaServer(MediaServerItem mediaServer) { + this.mediaServer = mediaServer; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public MediaInfo getMediaInfo() { + return mediaInfo; + } + + public void setMediaInfo(MediaInfo mediaInfo) { + this.mediaInfo = mediaInfo; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public RecordInfo getRecordInfo() { + return recordInfo; + } + + public void setRecordInfo(RecordInfo recordInfo) { + this.recordInfo = recordInfo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookSubscribe.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookSubscribe.java new file mode 100644 index 0000000..745389c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookSubscribe.java @@ -0,0 +1,107 @@ +package com.yfd.monitor.media.event.hook; + +import com.yfd.monitor.media.event.media.*; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * zlm hook事件的参数 + * @author lin + */ +@Component +public class HookSubscribe { + + /** + * 订阅数据过期时间 + */ + private final long subscribeExpire = 5 * 60 * 1000; + + @FunctionalInterface + public interface Event{ + void response(HookData data); + } + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaArrivalEvent event) { + if (event.getSchema() == null || "rtsp".equals(event.getSchema())) { + sendNotify(HookType.on_media_arrival, event); + } + + } + + /** + * 流结束事件 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaDepartureEvent event) { + if (event.getSchema() == null || "rtsp".equals(event.getSchema())) { + sendNotify(HookType.on_media_departure, event); + } + + } + /** + * 推流鉴权事件 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaPublishEvent event) { + sendNotify(HookType.on_publish, event); + } + /** + * 生成录像文件事件 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaRecordMp4Event event) { + sendNotify(HookType.on_record_mp4, event); + } + + private final Map allSubscribes = new ConcurrentHashMap<>(); + private final Map allHook = new ConcurrentHashMap<>(); + + private void sendNotify(HookType hookType, MediaEvent event) { + Hook paramHook = Hook.getInstance(hookType, event.getApp(), event.getStream(), event.getMediaServer().getId()); + Event hookSubscribeEvent = allSubscribes.get(paramHook.toString()); + if (hookSubscribeEvent != null) { + HookData data = HookData.getInstance(event); + hookSubscribeEvent.response(data); + } + } + + public void addSubscribe(Hook hook, HookSubscribe.Event event) { + if (hook.getExpireTime() == null) { + hook.setExpireTime(System.currentTimeMillis() + subscribeExpire); + } + allSubscribes.put(hook.toString(), event); + allHook.put(hook.toString(), hook); + } + + public void removeSubscribe(Hook hook) { + allSubscribes.remove(hook.toString()); + allHook.remove(hook.toString()); + } + + /** + * 对订阅数据进行过期清理 + */ + @Scheduled(fixedRate=subscribeExpire) //每5分钟执行一次 + public void execute(){ + long expireTime = System.currentTimeMillis(); + for (Hook hook : allHook.values()) { + if (hook.getExpireTime() < expireTime) { + allSubscribes.remove(hook.toString()); + allHook.remove(hook.toString()); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookType.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookType.java new file mode 100644 index 0000000..52a46ea --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/hook/HookType.java @@ -0,0 +1,15 @@ +package com.yfd.monitor.media.event.hook; + +/** + * hook类型 + * @author lin + */ + +public enum HookType { + + on_publish, + on_record_mp4, + on_media_arrival, + on_media_departure, + on_rtp_server_timeout, +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaArrivalEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaArrivalEvent.java new file mode 100644 index 0000000..2f6a71c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaArrivalEvent.java @@ -0,0 +1,46 @@ +package com.yfd.monitor.media.event.media; + +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; + +/** + * 流到来事件 + */ +public class MediaArrivalEvent extends MediaEvent { + public MediaArrivalEvent(Object source) { + super(source); + } + + public static MediaArrivalEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServerItem mediaServer){ + MediaArrivalEvent mediaArrivalEvent = new MediaArrivalEvent(source); + mediaArrivalEvent.setMediaInfo(MediaInfo.getInstance(hookParam, mediaServer)); + mediaArrivalEvent.setApp(hookParam.getApp()); + mediaArrivalEvent.setStream(hookParam.getStream()); + mediaArrivalEvent.setMediaServer(mediaServer); + mediaArrivalEvent.setSchema(hookParam.getSchema()); + mediaArrivalEvent.setCallId(hookParam.getCallId()); + return mediaArrivalEvent; + } + + private MediaInfo mediaInfo; + + private String callId; + + public MediaInfo getMediaInfo() { + return mediaInfo; + } + + public void setMediaInfo(MediaInfo mediaInfo) { + this.mediaInfo = mediaInfo; + } + + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaDepartureEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaDepartureEvent.java new file mode 100644 index 0000000..7fe6f5d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaDepartureEvent.java @@ -0,0 +1,22 @@ +package com.yfd.monitor.media.event.media; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; + +/** + * 流离开事件 + */ +public class MediaDepartureEvent extends MediaEvent { + public MediaDepartureEvent(Object source) { + super(source); + } + + public static MediaDepartureEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServerItem mediaServer){ + MediaDepartureEvent mediaDepartureEven = new MediaDepartureEvent(source); + mediaDepartureEven.setApp(hookParam.getApp()); + mediaDepartureEven.setStream(hookParam.getStream()); + mediaDepartureEven.setSchema(hookParam.getSchema()); + mediaDepartureEven.setMediaServer(mediaServer); + return mediaDepartureEven; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaEvent.java new file mode 100644 index 0000000..0f7149c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaEvent.java @@ -0,0 +1,57 @@ +package com.yfd.monitor.media.event.media; + + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import org.springframework.context.ApplicationEvent; + +/** + * 流到来事件 + */ +public class MediaEvent extends ApplicationEvent { + + public MediaEvent(Object source) { + super(source); + } + + private String app; + + private String stream; + + private MediaServerItem mediaServer; + + private String schema; + + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public MediaServerItem getMediaServer() { + return mediaServer; + } + + public void setMediaServer(MediaServerItem mediaServer) { + this.mediaServer = mediaServer; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaNotFoundEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaNotFoundEvent.java new file mode 100644 index 0000000..0b1848f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaNotFoundEvent.java @@ -0,0 +1,22 @@ +package com.yfd.monitor.media.event.media; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamNotFoundHookParam; + +/** + * 流未找到 + */ +public class MediaNotFoundEvent extends MediaEvent { + public MediaNotFoundEvent(Object source) { + super(source); + } + + public static MediaNotFoundEvent getInstance(Object source, OnStreamNotFoundHookParam hookParam, MediaServerItem mediaServer){ + MediaNotFoundEvent mediaDepartureEven = new MediaNotFoundEvent(source); + mediaDepartureEven.setApp(hookParam.getApp()); + mediaDepartureEven.setStream(hookParam.getStream()); + mediaDepartureEven.setSchema(hookParam.getSchema()); + mediaDepartureEven.setMediaServer(mediaServer); + return mediaDepartureEven; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaPublishEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaPublishEvent.java new file mode 100644 index 0000000..1591466 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaPublishEvent.java @@ -0,0 +1,33 @@ +package com.yfd.monitor.media.event.media; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnPublishHookParam; + +/** + * 推流鉴权事件 + */ +public class MediaPublishEvent extends MediaEvent { + public MediaPublishEvent(Object source) { + super(source); + } + + public static MediaPublishEvent getInstance(Object source, OnPublishHookParam hookParam, MediaServerItem mediaServer){ + MediaPublishEvent mediaPublishEvent = new MediaPublishEvent(source); + mediaPublishEvent.setApp(hookParam.getApp()); + mediaPublishEvent.setStream(hookParam.getStream()); + mediaPublishEvent.setMediaServer(mediaServer); + mediaPublishEvent.setSchema(hookParam.getSchema()); + mediaPublishEvent.setParams(hookParam.getParams()); + return mediaPublishEvent; + } + + private String params; + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaRecordMp4Event.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaRecordMp4Event.java new file mode 100644 index 0000000..2a6d7ba --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaRecordMp4Event.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.media.event.media; + +import com.yfd.monitor.media.bean.RecordInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnRecordMp4HookParam; + +/** + * 录像文件生成事件 + */ +public class MediaRecordMp4Event extends MediaEvent { + public MediaRecordMp4Event(Object source) { + super(source); + } + + private RecordInfo recordInfo; + + public static MediaRecordMp4Event getInstance(Object source, OnRecordMp4HookParam hookParam, MediaServerItem mediaServer){ + MediaRecordMp4Event mediaRecordMp4Event = new MediaRecordMp4Event(source); + mediaRecordMp4Event.setApp(hookParam.getApp()); + mediaRecordMp4Event.setStream(hookParam.getStream()); + RecordInfo recordInfo = RecordInfo.getInstance(hookParam); + mediaRecordMp4Event.setRecordInfo(recordInfo); + mediaRecordMp4Event.setMediaServer(mediaServer); + return mediaRecordMp4Event; + } + + public RecordInfo getRecordInfo() { + return recordInfo; + } + + public void setRecordInfo(RecordInfo recordInfo) { + this.recordInfo = recordInfo; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaRtpServerTimeoutEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaRtpServerTimeoutEvent.java new file mode 100644 index 0000000..a52fa23 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/media/MediaRtpServerTimeoutEvent.java @@ -0,0 +1,22 @@ +package com.yfd.monitor.media.event.media; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; + +/** + * RtpServer收流超时事件 + */ +public class MediaRtpServerTimeoutEvent extends MediaEvent { + public MediaRtpServerTimeoutEvent(Object source) { + super(source); + } + + public static MediaRtpServerTimeoutEvent getInstance(Object source, OnStreamChangedHookParam hookParam, MediaServerItem mediaServer){ + MediaRtpServerTimeoutEvent mediaDepartureEven = new MediaRtpServerTimeoutEvent(source); + mediaDepartureEven.setApp(hookParam.getApp()); + mediaDepartureEven.setStream(hookParam.getStream()); + mediaDepartureEven.setSchema(hookParam.getSchema()); + mediaDepartureEven.setMediaServer(mediaServer); + return mediaDepartureEven; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaSendRtpStoppedEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaSendRtpStoppedEvent.java new file mode 100644 index 0000000..c656671 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaSendRtpStoppedEvent.java @@ -0,0 +1,53 @@ +package com.yfd.monitor.media.event.mediaServer; + + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamNotFoundHookParam; +import org.springframework.context.ApplicationEvent; + +/** + * 发送流停止事件 + */ +public class MediaSendRtpStoppedEvent extends ApplicationEvent { + public MediaSendRtpStoppedEvent(Object source) { + super(source); + } + + private String app; + + private String stream; + + private MediaServerItem mediaServer; + + public static MediaSendRtpStoppedEvent getInstance(Object source, OnStreamNotFoundHookParam hookParam, MediaServerItem mediaServer){ + MediaSendRtpStoppedEvent mediaDepartureEven = new MediaSendRtpStoppedEvent(source); + mediaDepartureEven.setApp(hookParam.getApp()); + mediaDepartureEven.setStream(hookParam.getStream()); + mediaDepartureEven.setMediaServer(mediaServer); + return mediaDepartureEven; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public MediaServerItem getMediaServer() { + return mediaServer; + } + + public void setMediaServer(MediaServerItem mediaServer) { + this.mediaServer = mediaServer; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerChangeEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerChangeEvent.java new file mode 100644 index 0000000..74e625e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerChangeEvent.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.media.event.mediaServer; + + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import org.springframework.context.ApplicationEvent; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MediaServerChangeEvent extends ApplicationEvent { + + public MediaServerChangeEvent(Object source) { + super(source); + } + + private List mediaServerItemList; + + public List getMediaServerItemList() { + return mediaServerItemList; + } + + public void setMediaServerItemList(List mediaServerItemList) { + this.mediaServerItemList = mediaServerItemList; + } + + public void setMediaServerItemList(MediaServerItem... mediaServerItemArray) { + this.mediaServerItemList = new ArrayList<>(); + this.mediaServerItemList.addAll(Arrays.asList(mediaServerItemArray)); + } + + public void setMediaServerItem(List mediaServerItemList) { + this.mediaServerItemList = mediaServerItemList; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerDeleteEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerDeleteEvent.java new file mode 100644 index 0000000..049e1c3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerDeleteEvent.java @@ -0,0 +1,11 @@ +package com.yfd.monitor.media.event.mediaServer; + +/** + * zlm在线事件 + */ +public class MediaServerDeleteEvent extends MediaServerEventAbstract { + + public MediaServerDeleteEvent(Object source) { + super(source); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerEventAbstract.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerEventAbstract.java new file mode 100644 index 0000000..9626ee1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerEventAbstract.java @@ -0,0 +1,24 @@ +package com.yfd.monitor.media.event.mediaServer; + +import org.springframework.context.ApplicationEvent; + +public abstract class MediaServerEventAbstract extends ApplicationEvent { + + + private static final long serialVersionUID = 1L; + + private String mediaServerId; + + + public MediaServerEventAbstract(Object source) { + super(source); + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerOfflineEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerOfflineEvent.java new file mode 100644 index 0000000..61e0a27 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerOfflineEvent.java @@ -0,0 +1,11 @@ +package com.yfd.monitor.media.event.mediaServer; + +/** + * zlm离线事件类 + */ +public class MediaServerOfflineEvent extends MediaServerEventAbstract { + + public MediaServerOfflineEvent(Object source) { + super(source); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerOnlineEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerOnlineEvent.java new file mode 100644 index 0000000..8589d21 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerOnlineEvent.java @@ -0,0 +1,11 @@ +package com.yfd.monitor.media.event.mediaServer; + +/** + * zlm在线事件 + */ +public class MediaServerOnlineEvent extends MediaServerEventAbstract { + + public MediaServerOnlineEvent(Object source) { + super(source); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerStatusEventListener.java b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerStatusEventListener.java new file mode 100644 index 0000000..b6e8261 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/event/mediaServer/MediaServerStatusEventListener.java @@ -0,0 +1,54 @@ +package com.yfd.monitor.media.event.mediaServer; + + +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.service.IStreamPushService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源: + * 1、设备主动注销,发送注销指令 + * 2、设备未知原因离线,心跳超时 + * @author: swwheihei + * @date: 2020年5月6日 下午1:51:23 + */ +@Component +public class MediaServerStatusEventListener { + + private final static Logger logger = LoggerFactory.getLogger(MediaServerStatusEventListener.class); + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IPlayService playService; + + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaServerOnlineEvent event) { + logger.info("[媒体节点] 上线 ID:" + event.getMediaServerId()); + streamPushService.zlmServerOnline(event.getMediaServerId()); + streamProxyService.zlmServerOnline(event.getMediaServerId()); + playService.zlmServerOnline(event.getMediaServerId()); + } + + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaServerOfflineEvent event) { + + logger.info("[媒体节点] 离线,ID:" + event.getMediaServerId()); + // 处理ZLM离线 + streamProxyService.zlmServerOffline(event.getMediaServerId()); + streamPushService.zlmServerOffline(event.getMediaServerId()); + playService.zlmServerOffline(event.getMediaServerId()); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/service/IMediaNodeServerService.java b/riis-monitor/src/main/java/com/yfd/monitor/media/service/IMediaNodeServerService.java new file mode 100644 index 0000000..6e55f73 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/service/IMediaNodeServerService.java @@ -0,0 +1,68 @@ +package com.yfd.monitor.media.service; + + + +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.vmanager.bean.WVPResult; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public interface IMediaNodeServerService { + int createRTPServer(MediaServerItem mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, + Boolean disableAudio, Boolean reUsePort, Integer tcpMode); + + void closeRtpServer(MediaServerItem mediaServer, String streamId); + + void closeRtpServer(MediaServerItem mediaServer, String streamId, CommonCallback callback); + + void closeStreams(MediaServerItem mediaServer, String app, String stream); + + Boolean updateRtpServerSSRC(MediaServerItem mediaServer, String stream, String ssrc); + + boolean checkNodeId(MediaServerItem mediaServer); + + void online(MediaServerItem mediaServer); + + MediaServerItem checkMediaServer(String ip, int port, String secret); + + boolean stopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc); + + boolean initStopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc); + + boolean deleteRecordDirectory(MediaServerItem mediaServer, String app, String stream, String date, String fileName); + + List getMediaList(MediaServerItem mediaServer, String app, String stream, String callId); + + Boolean connectRtpServer(MediaServerItem mediaServer, String address, int port, String stream); + + void getSnap(MediaServerItem mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) throws IOException; + + MediaInfo getMediaInfo(MediaServerItem mediaServer, String app, String stream); + + Boolean pauseRtpCheck(MediaServerItem mediaServer, String streamKey); + + Boolean resumeRtpCheck(MediaServerItem mediaServer, String streamKey); + + String getFfmpegCmd(MediaServerItem mediaServer, String cmdKey); + + WVPResult addFFmpegSource(MediaServerItem mediaServer, String srcUrl, String dstUrl, int timeoutMs, + boolean enableAudio, boolean enableMp4, String ffmpegCmdKey); + + WVPResult addStreamProxy(MediaServerItem mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType); + + Boolean delFFmpegSource(MediaServerItem mediaServer, String streamKey); + + Boolean delStreamProxy(MediaServerItem mediaServer, String streamKey); + + Map getFFmpegCMDs(MediaServerItem mediaServer); + + void startSendRtpPassive(MediaServerItem mediaServer, SendRtpItem sendRtpItem, Integer timeout); + + void startSendRtpStream(MediaServerItem mediaServer, SendRtpItem sendRtpItem); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/service/IMediaServerService.java b/riis-monitor/src/main/java/com/yfd/monitor/media/service/IMediaServerService.java new file mode 100644 index 0000000..82e2590 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/service/IMediaServerService.java @@ -0,0 +1,160 @@ +package com.yfd.monitor.media.service; + + + +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.bean.MediaServerLoad; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.vmanager.bean.WVPResult; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * 媒体服务节点 + */ +public interface IMediaServerService { + + List getAllOnlineList(); + + List getAll(); + + List getAllFromDatabase(); + + List getAllOnline(); + + MediaServerItem getOne(String generalMediaServerId); + + void syncCatchFromDatabase(); + + MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist); + + void updateVmServer(List mediaServerItemList); + + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, + boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, + Boolean reUsePort, Integer tcpMode); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback callback); + Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc); + + void closeRTPServer(String mediaServerId, String streamId); + + void clearRTPServer(MediaServerItem mediaServerItem); + + void update(MediaServerItem mediaSerItem); + + void addCount(String mediaServerId); + + void removeCount(String mediaServerId); + + void releaseSsrc(String mediaServerItemId, String ssrc); + + void clearMediaServerForOnline(); + + void add(MediaServerItem mediaSerItem); + + void resetOnlineServerItem(MediaServerItem serverItem); + + MediaServerItem checkMediaServer(String ip, int port, String secret, String type); + + boolean checkMediaRecordServer(String ip, int port); + + void delete(String id); + + MediaServerItem getDefaultMediaServer(); + + MediaServerLoad getLoad(MediaServerItem mediaServerItem); + + List getAllWithAssistPort(); + + MediaServerItem getOneFromDatabase(String id); + + boolean stopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc); + + boolean initStopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc); + + boolean deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName); + + List getMediaList(MediaServerItem mediaInfo, String app, String stream, String callId); + + Boolean connectRtpServer(MediaServerItem mediaServerItem, String address, int port, String stream); + + void getSnap(MediaServerItem mediaServerItemInuse, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) throws IOException; + + MediaInfo getMediaInfo(MediaServerItem mediaServerItem, String app, String stream); + + Boolean pauseRtpCheck(MediaServerItem mediaServerItem, String streamKey); + + boolean resumeRtpCheck(MediaServerItem mediaServerItem, String streamKey); + + String getFfmpegCmd(MediaServerItem mediaServer, String cmdKey); + + void closeStreams(MediaServerItem mediaServerItem, String app, String stream); + + WVPResult addFFmpegSource(MediaServerItem mediaServerItem, String srcUrl, String dstUrl, int timeoutMs, + boolean enableAudio, boolean enableMp4, String ffmpegCmdKey); + + WVPResult addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType); + + Boolean delFFmpegSource(MediaServerItem mediaServerItem, String streamKey); + + Boolean delStreamProxy(MediaServerItem mediaServerItem, String streamKey); + + Map getFFmpegCMDs(MediaServerItem mediaServer); + + /** + * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority); + + + /** + * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay); + + Boolean isStreamReady(MediaServerItem mediaServer, String rtp, String streamId); + + void startSendRtpPassive(MediaServerItem mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, + Integer timeout); + + void startSendRtp(MediaServerItem mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem); + + SendRtpItem createSendRtpItem(MediaServerItem mediaServerItem, String addressStr, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean mediaTransmissionTCP, boolean rtcp); + + SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, + String app, String stream, String channelId, boolean tcp, boolean rtcp); + + MediaServerItem getMediaServerByAppAndStream(String app, String stream); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/service/impl/MediaServerImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/media/service/impl/MediaServerImpl.java new file mode 100644 index 0000000..028daaf --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/service/impl/MediaServerImpl.java @@ -0,0 +1,923 @@ +package com.yfd.monitor.media.service.impl; + +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.MediaConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.InviteStreamType; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.session.SSRCFactory; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.event.media.MediaArrivalEvent; +import com.yfd.monitor.media.event.media.MediaDepartureEvent; +import com.yfd.monitor.media.event.mediaServer.MediaServerChangeEvent; +import com.yfd.monitor.media.event.mediaServer.MediaServerDeleteEvent; +import com.yfd.monitor.media.service.IMediaNodeServerService; +import com.yfd.monitor.media.service.IMediaServerService; +import com.yfd.monitor.media.zlm.SendRtpPortManager; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamAuthorityInfo; +import com.yfd.monitor.media.zlm.dto.hook.OriginType; +import com.yfd.monitor.service.IInviteStreamService; +import com.yfd.monitor.service.bean.MediaServerLoad; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.MediaServerMapper; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.JsonUtil; +import com.yfd.monitor.utils.redis.RedisUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.WVPResult; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.*; + +/** + * 媒体服务器节点管理 + */ +@Service +public class MediaServerImpl implements IMediaServerService { + + private final static Logger logger = LoggerFactory.getLogger(MediaServerImpl.class); + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private UserSetting userSetting; + + @Autowired + private MediaServerMapper mediaServerMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private RedisTemplate redisTemplate; + + + @Autowired + private ApplicationEventPublisher applicationEventPublisher; + + @Autowired + private Map nodeServerServiceMap; + + @Autowired + private MediaConfig mediaConfig; + + @Autowired + private SendRtpPortManager sendRtpPortManager; + + + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @org.springframework.context.event.EventListener + public void onApplicationEvent(MediaArrivalEvent event) { + if ("rtsp".equals(event.getSchema())) { + logger.info("流变化:注册 app->{}, stream->{}", event.getApp(), event.getStream()); + addCount(event.getMediaServer().getId()); + String type = OriginType.values()[event.getMediaInfo().getOriginType()].getType(); + redisCatchStorage.addStream(event.getMediaServer(), type, event.getApp(), event.getStream(), event.getMediaInfo()); + } + } + + /** + * 流离开的处理 + */ + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(MediaDepartureEvent event) { + if ("rtsp".equals(event.getSchema())) { + logger.info("流变化:注销, app->{}, stream->{}", event.getApp(), event.getStream()); + removeCount(event.getMediaServer().getId()); + MediaInfo mediaInfo = redisCatchStorage.getStreamInfo1(event.getApp(), event.getStream(), event.getMediaServer().getId()); + if (mediaInfo == null) { + return; + } + String type = OriginType.values()[mediaInfo.getOriginType()].getType(); + redisCatchStorage.removeStream(mediaInfo.getMediaServer().getId(), type, event.getApp(), event.getStream()); + } + + } + + + /** + * 初始化 + */ + @Override + public void updateVmServer(List mediaServerList) { + logger.info("[媒体服务节点] 缓存初始化 "); + for (MediaServerItem mediaServer : mediaServerList) { + if (ObjectUtils.isEmpty(mediaServer.getId())) { + continue; + } + // 更新 + if (!ssrcFactory.hasMediaServerSSRC(mediaServer.getId())) { + ssrcFactory.initMediaServerSSRC(mediaServer.getId(), null); + } + // 查询redis是否存在此mediaServer + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServer.getId(); + Boolean hasKey = redisTemplate.hasKey(key); + if (hasKey != null && ! hasKey) { + redisTemplate.opsForValue().set(key, mediaServer); + } + } + } + + + @Override + public SSRCInfo openRTPServer(MediaServerItem mediaServer, String streamId, String presetSsrc, boolean ssrcCheck, + boolean isPlayback, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) { + if (mediaServer == null || mediaServer.getId() == null) { + logger.info("[openRTPServer] 失败, mediaServer == null || mediaServer.getId() == null"); + return null; + } + // 获取mediaServer可用的ssrc + String ssrc; + if (presetSsrc != null) { + ssrc = presetSsrc; + }else { + if (isPlayback) { + ssrc = ssrcFactory.getPlayBackSsrc(mediaServer.getId()); + }else { + ssrc = ssrcFactory.getPlaySsrc(mediaServer.getId()); + } + } + + if (streamId == null) { + streamId = String.format("%08x", Long.parseLong(ssrc)).toUpperCase(); + } + if (ssrcCheck && tcpMode > 0) { + // 目前zlm不支持 tcp模式更新ssrc,暂时关闭ssrc校验 + logger.warn("[openRTPServer] 平台对接时下级可能自定义ssrc,但是tcp模式zlm收流目前无法更新ssrc,可能收流超时,此时请使用udp收流或者关闭ssrc校验"); + } + int rtpServerPort; + if (mediaServer.isRtpEnable()) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[openRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return null; + } + rtpServerPort = mediaNodeServerService.createRTPServer(mediaServer, streamId, ssrcCheck ? Long.parseLong(ssrc) : 0, port, onlyAuto, disableAudio, reUsePort, tcpMode); + } else { + rtpServerPort = mediaServer.getRtpProxyPort(); + } + return new SSRCInfo(rtpServerPort, ssrc, streamId); + } + + @Override + public void closeRTPServer(MediaServerItem mediaServer, String streamId) { + if (mediaServer == null) { + return; + } + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return; + } + mediaNodeServerService.closeRtpServer(mediaServer, streamId); + } + + @Override + public void closeRTPServer(MediaServerItem mediaServer, String streamId, CommonCallback callback) { + if (mediaServer == null) { + callback.run(false); + return; + } + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return; + } + mediaNodeServerService.closeRtpServer(mediaServer, streamId, callback); + } + + @Override + public void closeRTPServer(String mediaServerId, String streamId) { + MediaServerItem mediaServer = this.getOne(mediaServerId); + if (mediaServer == null) { + return; + } + if (mediaServer.isRtpEnable()) { + closeRTPServer(mediaServer, streamId); + } + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return; + } + mediaNodeServerService.closeStreams(mediaServer, "rtp", streamId); + } + + @Override + public Boolean updateRtpServerSSRC(MediaServerItem mediaServer, String streamId, String ssrc) { + if (mediaServer == null) { + return false; + } + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[updateRtpServerSSRC] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + return mediaNodeServerService.updateRtpServerSSRC(mediaServer, streamId, ssrc); + } + + @Override + public void releaseSsrc(String mediaServerId, String ssrc) { + MediaServerItem mediaServer = getOne(mediaServerId); + if (mediaServer == null || ssrc == null) { + return; + } + ssrcFactory.releaseSsrc(mediaServerId, ssrc); + } + + /** + * 媒体服务节点 重启后重置他的推流信息, TODO 给正在使用的设备发送停止命令 + */ + @Override + public void clearRTPServer(MediaServerItem mediaServer) { + ssrcFactory.reset(mediaServer.getId()); + } + + + @Override + public void update(MediaServerItem mediaSerItem) { + mediaServerMapper.update(mediaSerItem); + MediaServerItem mediaServerInRedis = getOne(mediaSerItem.getId()); + MediaServerItem mediaServerInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId()); + if (mediaServerInDataBase == null) { + return; + } + mediaServerInDataBase.setStatus(mediaSerItem.isStatus()); + if (mediaServerInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaServerInDataBase.getId())) { + ssrcFactory.initMediaServerSSRC(mediaServerInDataBase.getId(),null); + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerInDataBase.getId(); + redisTemplate.opsForValue().set(key, mediaServerInDataBase); + if (mediaServerInDataBase.isStatus()) { + resetOnlineServerItem(mediaServerInDataBase); + }else { + // 发送事件 + MediaServerChangeEvent event = new MediaServerChangeEvent(this); + event.setMediaServerItemList(mediaServerInDataBase); + applicationEventPublisher.publishEvent(event); + } + } + + + @Override + public List getAllOnlineList() { + List result = new ArrayList<>(); + List mediaServerKeys = RedisUtil.scan(redisTemplate, String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + ":" )); + String onlineKey = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + for (Object mediaServerKey : mediaServerKeys) { + String key = (String) mediaServerKey; + MediaServerItem mediaServer = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class); + if (Objects.isNull(mediaServer)) { + continue; + } + // 检查状态 + Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServer.getId()); + if (aDouble != null) { + mediaServer.setStatus(true); + } + result.add(mediaServer); + } + result.sort((serverItem1, serverItem2)->{ + int sortResult = 0; + LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter); + LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter); + + sortResult = localDateTime1.compareTo(localDateTime2); + return sortResult; + }); + return result; + } + + @Override + public List getAll() { + List mediaServerList = mediaServerMapper.queryAll(); + if (mediaServerList.isEmpty()) { + return new ArrayList<>(); + } + for (MediaServerItem mediaServer : mediaServerList) { + MediaServerItem mediaServerInRedis = getOne(mediaServer.getId()); + if (mediaServerInRedis != null) { + mediaServer.setStatus(mediaServerInRedis.isStatus()); + } + } + return mediaServerList; + } + + + @Override + public List getAllFromDatabase() { + return mediaServerMapper.queryAll(); + } + + @Override + public List getAllOnline() { + String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + Set mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1); + + List result = new ArrayList<>(); + if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) { + for (Object mediaServerId : mediaServerIdSet) { + String mediaServerIdStr = (String) mediaServerId; + String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerIdStr; + result.add((MediaServerItem) redisTemplate.opsForValue().get(serverKey)); + } + } + Collections.reverse(result); + return result; + } + + /** + * 获取单个媒体服务节点服务器 + * @param mediaServerId 服务id + * @return mediaServer + */ + @Override + public MediaServerItem getOne(String mediaServerId) { + if (mediaServerId == null) { + return null; + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerId; + return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class); + } + + + @Override + public MediaServerItem getDefaultMediaServer() { + return mediaServerMapper.queryDefault(); + } + + @Override + public void clearMediaServerForOnline() { + String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + redisTemplate.delete(key); + } + + @Override + public void add(MediaServerItem mediaServer) { + mediaServer.setCreateTime(DateUtil.getNow()); + mediaServer.setUpdateTime(DateUtil.getNow()); + if (mediaServer.getHookAliveInterval() == null || mediaServer.getHookAliveInterval() == 0F) { + mediaServer.setHookAliveInterval(10F); + } + if (mediaServer.getType() == null) { + logger.info("[添加媒体节点] 失败, mediaServer的类型:为空"); + return; + } + if (mediaServerMapper.queryOne(mediaServer.getId()) != null) { + logger.info("[添加媒体节点] 失败, 媒体服务ID已存在,请修改媒体服务器配置, {}", mediaServer.getId()); + throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置"); + } + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[添加媒体节点] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return; + } + + mediaServerMapper.add(mediaServer); + if (mediaServer.isStatus()) { + mediaNodeServerService.online(mediaServer); + }else { + // 发送事件 + MediaServerChangeEvent event = new MediaServerChangeEvent(this); + event.setMediaServerItemList(mediaServer); + applicationEventPublisher.publishEvent(event); + } + } + + @Override + public void resetOnlineServerItem(MediaServerItem serverItem) { + // 更新缓存 + String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + // 使用zset的分数作为当前并发量, 默认值设置为0 + if (redisTemplate.opsForZSet().score(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置 + redisTemplate.opsForZSet().add(key, serverItem.getId(), 0L); + // 查询服务流数量 + int count = getMediaList(serverItem); + redisTemplate.opsForZSet().add(key, serverItem.getId(), count); + }else { + clearRTPServer(serverItem); + } + } + + private int getMediaList(MediaServerItem serverItem) { + + return 0; + } + + + @Override + public void addCount(String mediaServerId) { + if (mediaServerId == null) { + return; + } + String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + redisTemplate.opsForZSet().incrementScore(key, mediaServerId, 1); + + } + + @Override + public void removeCount(String mediaServerId) { + String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + redisTemplate.opsForZSet().incrementScore(key, mediaServerId, - 1); + } + + /** + * 获取负载最低的节点 + * @return mediaServer + */ + @Override + public MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist) { + String key = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); + Long size = redisTemplate.opsForZSet().zCard(key); + if (size == null || size == 0) { + logger.info("获取负载最低的节点时无在线节点"); + return null; + } + + // 获取分数最低的,及并发最低的 + Set objects = redisTemplate.opsForZSet().range(key, 0, -1); + ArrayList mediaServerObjectS = new ArrayList<>(objects); + MediaServerItem mediaServer = null; + if (hasAssist == null) { + String mediaServerId = (String)mediaServerObjectS.get(0); + mediaServer = getOne(mediaServerId); + }else if (hasAssist) { + for (Object mediaServerObject : mediaServerObjectS) { + String mediaServerId = (String)mediaServerObject; + MediaServerItem serverItem = getOne(mediaServerId); + if (serverItem.getRecordAssistPort() > 0) { + mediaServer = serverItem; + break; + } + } + }else if (!hasAssist) { + for (Object mediaServerObject : mediaServerObjectS) { + String mediaServerId = (String)mediaServerObject; + MediaServerItem serverItem = getOne(mediaServerId); + if (serverItem.getRecordAssistPort() == 0) { + mediaServer = serverItem; + break; + } + } + } + + return mediaServer; + } + + @Override + public MediaServerItem checkMediaServer(String ip, int port, String secret, String type) { + if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在"); + } + + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(type); + if (mediaNodeServerService == null) { + logger.info("[closeRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", type); + return null; + } + MediaServerItem mediaServer = mediaNodeServerService.checkMediaServer(ip, port, secret); + if (mediaServer != null) { + if (mediaServerMapper.queryOne(mediaServer.getId()) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + mediaServer.getId() + " ] 已存在,请修改媒体服务器配置"); + } + } + return mediaServer; + } + + @Override + public boolean checkMediaRecordServer(String ip, int port) { + boolean result = false; + OkHttpClient client = new OkHttpClient(); + String url = String.format("http://%s:%s/index/api/record", ip, port); + Request request = new Request.Builder() + .get() + .url(url) + .build(); + try { + Response response = client.newCall(request).execute(); + if (response != null) { + result = true; + } + } catch (Exception e) {} + + return result; + } + + @Override + public void delete(String id) { + mediaServerMapper.delOne(id); + redisTemplate.opsForZSet().remove(VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(), id); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + id; + redisTemplate.delete(key); + // 发送节点移除通知 + MediaServerDeleteEvent event = new MediaServerDeleteEvent(this); + event.setMediaServerId(id); + applicationEventPublisher.publishEvent(event); + } + + @Override + public MediaServerItem getOneFromDatabase(String mediaServerId) { + return mediaServerMapper.queryOne(mediaServerId); + } + + @Override + public void syncCatchFromDatabase() { + List allInCatch = getAllOnlineList(); + List allInDatabase = mediaServerMapper.queryAll(); + Map mediaServerMap = new HashMap<>(); + + for (MediaServerItem mediaServer : allInDatabase) { + mediaServerMap.put(mediaServer.getId(), mediaServer); + } + for (MediaServerItem mediaServer : allInCatch) { + // 清除数据中不存在但redis缓存数据 + if (!mediaServerMap.containsKey(mediaServer.getId())) { + delete(mediaServer.getId()); + } + } + } + + @Override + public MediaServerLoad getLoad(MediaServerItem mediaServer) { + MediaServerLoad result = new MediaServerLoad(); + result.setId(mediaServer.getId()); + result.setPush(redisCatchStorage.getPushStreamCount(mediaServer.getId())); + result.setProxy(redisCatchStorage.getProxyStreamCount(mediaServer.getId())); + + result.setGbReceive(inviteStreamService.getStreamInfoCount(mediaServer.getId())); + result.setGbSend(redisCatchStorage.getGbSendCount(mediaServer.getId())); + return result; + } + + @Override + public List getAllWithAssistPort() { + return mediaServerMapper.queryAllWithAssistPort(); + } + + + @Override + public boolean stopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType()); + if (mediaNodeServerService == null) { + logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); + return false; + } + return mediaNodeServerService.stopSendRtp(mediaInfo, app, stream, ssrc); + } + + @Override + public boolean initStopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType()); + if (mediaNodeServerService == null) { + logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); + return false; + } + return mediaNodeServerService.initStopSendRtp(mediaInfo, app, stream, ssrc); + } + + @Override + public boolean deleteRecordDirectory(MediaServerItem mediaServer, String app, String stream, String date, String fileName) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + return mediaNodeServerService.deleteRecordDirectory(mediaServer, app, stream, date, fileName); + } + + @Override + public List getMediaList(MediaServerItem mediaServer, String app, String stream, String callId) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[getMediaList] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return new ArrayList<>(); + } + return mediaNodeServerService.getMediaList(mediaServer, app, stream, callId); + } + + @Override + public Boolean connectRtpServer(MediaServerItem mediaServer, String address, int port, String stream) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[connectRtpServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + return mediaNodeServerService.connectRtpServer(mediaServer, address, port, stream); + } + + @Override + public void getSnap(MediaServerItem mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) throws IOException { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[getSnap] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return; + } + mediaNodeServerService.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName); + } + + @Override + public MediaInfo getMediaInfo(MediaServerItem mediaServer, String app, String stream) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[getMediaInfo] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return null; + } + return mediaNodeServerService.getMediaInfo(mediaServer, app, stream); + } + + @Override + public Boolean pauseRtpCheck(MediaServerItem mediaServer, String streamKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + return mediaNodeServerService.pauseRtpCheck(mediaServer, streamKey); + } + + @Override + public boolean resumeRtpCheck(MediaServerItem mediaServer, String streamKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[pauseRtpCheck] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + return mediaNodeServerService.resumeRtpCheck(mediaServer, streamKey); + } + + @Override + public String getFfmpegCmd(MediaServerItem mediaServer, String cmdKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[getFfmpegCmd] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return null; + } + return mediaNodeServerService.getFfmpegCmd(mediaServer, cmdKey); + } + + @Override + public void closeStreams(MediaServerItem mediaServer, String app, String stream) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[closeStreams] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return; + } + mediaNodeServerService.closeStreams(mediaServer, app, stream); + } + + @Override + public WVPResult addFFmpegSource(MediaServerItem mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[addFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return WVPResult.fail(ErrorCode.ERROR400); + } + return mediaNodeServerService.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs, enableAudio, enableMp4, ffmpegCmdKey); + } + + @Override + public WVPResult addStreamProxy(MediaServerItem mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[addStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return WVPResult.fail(ErrorCode.ERROR400); + } + return mediaNodeServerService.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType); + } + + @Override + public Boolean delFFmpegSource(MediaServerItem mediaServer, String streamKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[delFFmpegSource] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + return mediaNodeServerService.delFFmpegSource(mediaServer, streamKey); + } + + @Override + public Boolean delStreamProxy(MediaServerItem mediaServerItem, String streamKey) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServerItem.getType()); + if (mediaNodeServerService == null) { + logger.info("[delStreamProxy] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServerItem.getType()); + return false; + } + return mediaNodeServerService.delStreamProxy(mediaServerItem, streamKey); + } + + @Override + public Map getFFmpegCMDs(MediaServerItem mediaServer) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[getFFmpegCMDs] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return new HashMap<>(); + } + return mediaNodeServerService.getFFmpegCMDs(mediaServer); + } + + @Override + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId) { + return getStreamInfoByAppAndStream(mediaServerItem, app, stream, mediaInfo, null, callId, true); + } + + @Override + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) { + StreamInfo streamInfo = null; + if (mediaServerId == null) { + mediaServerId = mediaConfig.getId(); + } + MediaServerItem mediaInfo = getOne(mediaServerId); + if (mediaInfo == null) { + return null; + } + String calld = null; + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); + if (streamAuthorityInfo != null) { + calld = streamAuthorityInfo.getCallId(); + } + List streamInfoList = getMediaList(mediaInfo, app, stream, calld); + if (streamInfoList.isEmpty()) { + return null; + }else { + return streamInfoList.get(0); + } + } + + + + @Override + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) { + return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority); + } + + @Override + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) { + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setStream(stream); + streamInfoResult.setApp(app); + if (addr == null) { + addr = mediaServer.getStreamIp(); + } + + streamInfoResult.setIp(addr); + streamInfoResult.setMediaServerId(mediaServer.getId()); + String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; + streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam); + + +// if ("abl".equals(mediaServer.getType())) { +// String flvFile = String.format("%s/%s.flv%s", app, stream, callIdParam); +// streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile); +// streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile); +// }else { +// String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam); +// streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile); +// streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile); +// } + + streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); +// streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay); + +// streamInfoResult.setMediaInfo(mediaInfo); + return streamInfoResult; + } + + @Override + public Boolean isStreamReady(MediaServerItem mediaServer, String rtp, String streamId) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[isStreamReady] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + MediaInfo mediaInfo = mediaNodeServerService.getMediaInfo(mediaServer, rtp, streamId); + return mediaInfo != null; + } + + @Override + public void startSendRtpPassive(MediaServerItem mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[startSendRtpPassive] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); + } + mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout); + sendPlatformStartPlayMsg(platform, sendRtpItem); + } + + @Override + public void startSendRtp(MediaServerItem mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[startSendRtpStream] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); + } + logger.info("[开始推流] rtp/{}, 目标={}:{},SSRC={}, RTCP={}", sendRtpItem.getStreamId(), + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isRtcp()); + mediaNodeServerService.startSendRtpStream(mediaServer, sendRtpItem); + if (platform != null) { + sendPlatformStartPlayMsg(platform, sendRtpItem); + } + + + } + + private void sendPlatformStartPlayMsg(ParentPlatform platform, SendRtpItem sendRtpItem) { + if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform != null) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStreamId(), + sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(), + sendRtpItem.getMediaServerId()); +// messageForPushChannel.setPlatFormIndex(platform.getId()); + redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel); + } + } + + @Override + public SendRtpItem createSendRtpItem(MediaServerItem mediaServer, String ip, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean isTcp, boolean rtcp) { + int localPort = sendRtpPortManager.getNextPort(mediaServer); + if (localPort == 0) { + return null; + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setPlatformId(deviceId); + sendRtpItem.setDeviceId(deviceId); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(isTcp); + sendRtpItem.setRtcp(rtcp); + sendRtpItem.setApp("rtp"); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); + sendRtpItem.setMediaServerId(mediaServer.getId()); + return sendRtpItem; + } + + @Override + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, + String app, String stream, String channelId, boolean tcp, boolean rtcp){ + + int localPort = sendRtpPortManager.getNextPort(serverItem); + if (localPort == 0) { + return null; + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setApp(app); +// sendRtpItem.setStream(stream); + sendRtpItem.setPlatformId(platformId); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(tcp); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); + sendRtpItem.setMediaServerId(serverItem.getId()); + sendRtpItem.setRtcp(rtcp); + return sendRtpItem; + } + + @Override + public MediaServerItem getMediaServerByAppAndStream(String app, String stream) { + List mediaServerList = getAll(); + for (MediaServerItem mediaServer : mediaServerList) { + MediaInfo mediaInfo = getMediaInfo(mediaServer, app, stream); + if (mediaInfo != null) { + return mediaServer; + } + } + return null; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/AssistRESTfulUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/AssistRESTfulUtils.java new file mode 100644 index 0000000..56c5138 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/AssistRESTfulUtils.java @@ -0,0 +1,148 @@ +package com.yfd.monitor.media.zlm; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import okhttp3.*; +import okhttp3.logging.HttpLoggingInterceptor; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.ConnectException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Component +public class AssistRESTfulUtils { + + private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class); + + public interface RequestCallback{ + void run(JSONObject response); + } + + private OkHttpClient getClient(){ + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); + if (logger.isDebugEnabled()) { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { + logger.debug("http请求参数:" + message); + }); + logging.setLevel(HttpLoggingInterceptor.Level.BASIC); + // OkHttp進行添加攔截器loggingInterceptor + httpClientBuilder.addInterceptor(logging); + } + return httpClientBuilder.build(); + } + + + public JSONObject sendGet(MediaServerItem mediaServerItem, String api, Map param, RequestCallback callback) { + OkHttpClient client = getClient(); + + if (mediaServerItem == null) { + return null; + } + if (mediaServerItem.getRecordAssistPort() <= 0) { + logger.warn("未启用Assist服务"); + return null; + } + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api)); + JSONObject responseJSON = null; + + if (param != null && param.keySet().size() > 0) { + stringBuffer.append("?"); + int index = 1; + for (String key : param.keySet()){ + if (param.get(key) != null) { + stringBuffer.append(key + "=" + param.get(key)); + if (index < param.size()) { + stringBuffer.append("&"); + } + } + index++; + } + } + + String url = stringBuffer.toString(); + Request request = new Request.Builder() + .get() + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON = JSON.parseObject(responseStr); + } + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } catch (ConnectException e) { + logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + logger.info("请检查media配置并确认Assist已启动..."); + }catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + }else { + client.newCall(request).enqueue(new Callback(){ + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response){ + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + logger.info("请检查media配置并确认Assist已启动..."); + } + }); + } + + + + return responseJSON; + } + + + public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){ + Map param = new HashMap<>(); + param.put("app",app); + param.put("stream",stream); + param.put("recordIng",true); + return sendGet(mediaServerItem, "api/record/file/duration",param, callback); + } + + public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){ + Map param = new HashMap<>(); + return sendGet(mediaServerItem, "api/record/info",param, callback); + } + + public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){ + Map param = new HashMap<>(); + param.put("app",app); + param.put("stream",stream); + param.put("callId",callId); + return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/SendRtpPortManager.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/SendRtpPortManager.java new file mode 100644 index 0000000..99534da --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/SendRtpPortManager.java @@ -0,0 +1,136 @@ +package com.yfd.monitor.media.zlm; + + +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.utils.redis.RedisUtil; +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.support.atomic.RedisAtomicInteger; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class SendRtpPortManager { + + private final static Logger logger = LoggerFactory.getLogger(SendRtpPortManager.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private RedisTemplate redisTemplate; + + private final String KEY = "VM_MEDIA_SEND_RTP_PORT_"; + + public synchronized int getNextPort(MediaServerItem mediaServer) { + if (mediaServer == null) { + logger.warn("[发送端口管理] 参数错误,mediaServer为NULL"); + return -1; + } + String sendIndexKey = KEY + userSetting.getServerId() + "_" + mediaServer.getId(); + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + Map sendRtpItemMap = new HashMap<>(); + + for (Object o : queryResult) { + SendRtpItem sendRtpItem = (SendRtpItem) redisTemplate.opsForValue().get(o); + if (sendRtpItem != null) { + sendRtpItemMap.put(sendRtpItem.getLocalPort(), sendRtpItem); + } + } + String sendRtpPortRange = mediaServer.getRtpPortRange(); + int startPort; + int endPort; + if (sendRtpPortRange != null) { + String[] portArray = sendRtpPortRange.split(","); + if (portArray.length != 2 || !NumberUtils.isParsable(portArray[0]) || !NumberUtils.isParsable(portArray[1])) { + logger.warn("{}发送端口配置格式错误,自动使用50000-60000作为端口范围", mediaServer.getId()); + startPort = 50000; + endPort = 60000; + }else { + if ( Integer.parseInt(portArray[1]) - Integer.parseInt(portArray[0]) < 1) { + logger.warn("{}发送端口配置错误,结束端口至少比开始端口大一,自动使用50000-60000作为端口范围", mediaServer.getId()); + startPort = 50000; + endPort = 60000; + }else { + startPort = Integer.parseInt(portArray[0]); + endPort = Integer.parseInt(portArray[1]); + } + } + }else { + logger.warn("{}未设置发送端口默认值,自动使用50000-60000作为端口范围", mediaServer.getId()); + startPort = 50000; + endPort = 60000; + } + if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) { + logger.warn("{}获取redis连接信息失败", mediaServer.getId()); + return -1; + } +// RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); +// return redisAtomicInteger.getAndUpdate((current)->{ +// return getPort(current, startPort, endPort, checkPort-> !sendRtpItemMap.containsKey(checkPort)); +// }); + return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); + } + + private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Map sendRtpItemMap){ + RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); + if (redisAtomicInteger.get() < startPort) { + redisAtomicInteger.set(startPort); + return startPort; + }else { + int port = redisAtomicInteger.getAndIncrement(); + if (port > endPort) { + redisAtomicInteger.set(startPort); + if (sendRtpItemMap.containsKey(startPort)) { + return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); + }else { + return startPort; + } + } + if (sendRtpItemMap.containsKey(port)) { + return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); + }else { + return port; + } + } + + } + + interface CheckPortCallback{ + boolean check(int port); + } + + private int getPort(int current, int start, int end, CheckPortCallback checkPortCallback) { + if (current <= 0) { + if (start%2 == 0) { + current = start; + }else { + current = start + 1; + } + }else { + current += 2; + if (current > end) { + if (start%2 == 0) { + current = start; + }else { + current = start + 1; + } + } + } + if (!checkPortCallback.check(current)) { + return getPort(current + 2, start, end, checkPortCallback); + } + return current; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMHttpHookListener.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMHttpHookListener.java new file mode 100644 index 0000000..bcb87ad --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMHttpHookListener.java @@ -0,0 +1,717 @@ +package com.yfd.monitor.media.zlm; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.media.zlm.dto.HookType; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamAuthorityInfo; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.media.zlm.dto.hook.*; +import com.yfd.monitor.service.*; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.DeferredResultEx; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.apache.tomcat.util.security.MD5Encoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.util.DigestUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.servlet.http.HttpServletRequest; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * @description:针对 ZLMediaServer的hook事件监听 + * @date: 2020年5月8日 上午10:46:48 + */ +@RestController +@RequestMapping("/index/hook") +public class ZLMHttpHookListener { + + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform commanderFroPlatform; + + @Autowired + private IPlayService playService; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IMediaService mediaService; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private ZLMMediaListManager zlmMediaListManager; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IUserService userService; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + /** + * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 + */ + @ResponseBody + + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8") + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) { + +// logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId()); + + taskExecutor.execute(() -> { + List subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive); + JSONObject json = (JSONObject) JSON.toJSON(param); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, json); + } + } + }); + mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData()); + + return HookResult.SUCCESS(); + } + + /** + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 + */ + @ResponseBody + + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") + public HookResult onPlay(@RequestBody OnPlayHookParam param) { + if (logger.isDebugEnabled()) { + logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param); + } + String mediaServerId = param.getMediaServerId(); + + taskExecutor.execute(() -> { + JSONObject json = (JSONObject) JSON.toJSON(param); + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json); + if (subscribe != null) { + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + } + }); + if (!"rtp".equals(param.getApp())) { + Map paramMap = urlParamToMap(param.getParams()); + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) { + return new HookResult(401, "Unauthorized"); + } + } + + return HookResult.SUCCESS(); + } + + /** + * rtsp/rtmp/rtp推流鉴权事件。 + */ + @ResponseBody + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") + public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) { + + JSONObject json = (JSONObject) JSON.toJSON(param); + + logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param); + + String mediaServerId = json.getString("mediaServerId"); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + + if (!"rtp".equals(param.getApp())) { + if (userSetting.getPushAuthority()) { + // 推流鉴权 + if (param.getParams() == null) { + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)"); + return new HookResultForOnPublish(401, "Unauthorized"); + } + Map paramMap = urlParamToMap(param.getParams()); + if (ObjectUtil.isEmpty(paramMap.get("sign"))) { + paramMap.put("sign", DigestUtils.md5DigestAsHex(param.getParams().getBytes())); + } + String sign = paramMap.get("sign"); + if (sign == null) { + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)"); + return new HookResultForOnPublish(401, "Unauthorized"); + } + // 推流自定义播放鉴权码 + String callId = paramMap.get("callId"); + // 鉴权配置 + boolean hasAuthority = userService.checkPushAuthority(callId, sign); + if (!hasAuthority) { + logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign); + return new HookResultForOnPublish(401, "Unauthorized"); + } + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); + streamAuthorityInfo.setCallId(callId); + streamAuthorityInfo.setSign(sign); + // 鉴权通过 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); + // 通知assist新的callId + if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) { + taskExecutor.execute(() -> { + assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null); + }); + } + } + } else { + zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId()); + } + + + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS(); + if (!"rtp".equals(param.getApp())) { + result.setEnable_audio(true); + } + + taskExecutor.execute(() -> { + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); + if (subscribe != null) { + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } else { + new HookResultForOnPublish(1, "zlm not register"); + } + } + }); + + if ("rtp".equals(param.getApp())) { + result.setEnable_mp4(userSetting.getRecordSip()); +// result.setEnable_mp4(true); + } else { + result.setEnable_mp4(userSetting.isRecordPushLive()); + } + List ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream()); + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { + String deviceId = ssrcTransactionForAll.get(0).getDeviceId(); + String channelId = ssrcTransactionForAll.get(0).getChannelId(); + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + result.setEnable_audio(deviceChannel.isHasAudio()); + } + // 如果是录像下载就设置视频间隔十秒 + if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) { + result.setMp4_max_second(10); + result.setEnable_audio(true); + result.setEnable_mp4(true); + } + } + if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { + logger.info("推流时发现尚未设置录像路径,从assist服务中读取"); + JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null); + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) { + JSONObject dataJson = info.getJSONObject("data"); + if (dataJson != null) { + String recordPath = dataJson.getString("record"); + userSetting.setRecordPath(recordPath); + result.setMp4_save_path(recordPath); + // 修改zlm中的录像路径 + if (mediaInfo.isAutoConfig()) { + taskExecutor.execute(() -> { + mediaServerService.setZLMConfig(mediaInfo, false); + }); + } + } + } + } + return result; + } + + + /** + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) { + + if (param.isRegist()) { + logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + } else { + logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + } + + + JSONObject json = (JSONObject) JSON.toJSON(param); + taskExecutor.execute(() -> { + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); + if (subscribe != null) { + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + } + + List tracks = param.getTracks(); + // TODO 重构此处逻辑 + + if (param.isRegist()) { + // 处理流注册的鉴权信息 + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { + + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + if (streamAuthorityInfo == null) { + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); + } else { + streamAuthorityInfo.setOriginType(param.getOriginType()); + streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr()); + } + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); + } + } else { + redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream()); + } + + if ("rtsp".equals(param.getSchema())) { + // 更新流媒体负载信息 + if (param.isRegist()) { + mediaServerService.addCount(param.getMediaServerId()); + } else { + mediaServerService.removeCount(param.getMediaServerId()); + } + // 设置拉流代理上线/离线 + streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream()); + + if ("rtp".equals(param.getApp()) && !param.isRegist()) { + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(param.getStream()); + if (streamInfo != null) { + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + } else { + streamInfo = redisCatchStorage.queryPlayback(null, null, param.getStream(), null); + if (streamInfo != null) { + redisCatchStorage.stopPlayback(streamInfo.getDeviceID(), streamInfo.getChannelId(), + streamInfo.getStream(), null); + } + } + } else { + if (!"rtp".equals(param.getApp())) { + String type = OriginType.values()[param.getOriginType()].getType(); + MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + + if (mediaServerItem != null) { + if (param.isRegist()) { + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + String callId = null; + if (streamAuthorityInfo != null) { + callId = streamAuthorityInfo.getCallId(); + } + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem, + param.getApp(), param.getStream(), tracks, callId); + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream)); + redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param); + if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { + param.setSeverId(userSetting.getServerId()); + zlmMediaListManager.addPush(param); + } + } else { + // 兼容流注销时类型从redis记录获取 + OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(param.getApp(), param.getStream(), param.getMediaServerId()); + if (onStreamChangedHookParam != null) { + type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType(); + redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream()); + } + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream()); + if (gbStream != null) { +// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF); + } + zlmMediaListManager.removeMedia(param.getApp(), param.getStream()); + } + if (type != null) { + // 发送流变化redis消息 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", param.getApp()); + jsonObject.put("stream", param.getStream()); + jsonObject.put("register", param.isRegist()); + jsonObject.put("mediaServerId", param.getMediaServerId()); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + } + } + } + } + if (!param.isRegist()) { + List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + if (sendRtpItem != null && sendRtpItem.getApp().equals(param.getApp())) { + String platformId = sendRtpItem.getPlatformId(); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + Device device = deviceService.getDevice(platformId); + + try { + if (platform != null) { + commanderFroPlatform.streamByeCmd(platform, sendRtpItem); + redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + } else { + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId()); + } + } catch (SipException | InvalidArgumentException | ParseException | + SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + } + } + } + } + } + }); + + return HookResult.SUCCESS(); + } + + /** + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") + public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) { + + logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + JSONObject ret = new JSONObject(); + ret.put("code", 0); + // 国标类型的流 + if ("rtp".equals(param.getApp())) { + ret.put("close", userSetting.getStreamOnDemand()); + // 国标流, 点播/录像回放/录像下载 + StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(param.getStream()); + // 点播 + if (streamInfoForPlayCatch != null) { + // 收到无人观看说明流也没有在往上级推送 + if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) { + List sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(streamInfoForPlayCatch.getChannelId()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ParentPlatform parentPlatform = + storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + try { + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + String serverGBId = sendRtpItem.getPlatformId(); + if (parentPlatform != null) { + serverGBId = parentPlatform.getServerGBId(); + } + redisCatchStorage.deleteSendRTPServer(serverGBId, sendRtpItem.getChannelId(), sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + } + } + } + Device device = deviceService.getDevice(streamInfoForPlayCatch.getDeviceID()); + if (device != null) { + try { + cmder.streamByeCmd(device, streamInfoForPlayCatch.getChannelId(), + streamInfoForPlayCatch.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage()); + } + } + + redisCatchStorage.stopPlay(streamInfoForPlayCatch); + storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId()); + return ret; + } + // 录像回放 + StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null); + if (streamInfoForPlayBackCatch != null) { + if (streamInfoForPlayBackCatch.isPause()) { + ret.put("close", false); + } else { + Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID()); + if (device != null) { + try { + cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(), + streamInfoForPlayBackCatch.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage()); + } + } + redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(), + streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null); + } + return ret; + } + // 录像下载 + StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null); + // 进行录像下载时无人观看不断流 + if (streamInfoForDownload != null) { + ret.put("close", false); + return ret; + } + } else { + // 非国标流 推流/拉流代理 + // 拉流代理 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyItem != null) { + if (streamProxyItem.isEnable_remove_none_reader()) { + // 无人观看自动移除 + ret.put("close", true); + streamProxyService.del(param.getApp(), param.getStream()); + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrc_url(); + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url); + } else if (streamProxyItem.isEnable_disable_none_reader()) { + // 无人观看停用 + ret.put("close", true); + // 修改数据 + streamProxyService.stop(param.getApp(), param.getStream()); + } else { + // 无人观看不做处理 + ret.put("close", false); + } + return ret; + } + // 推流具有主动性,暂时不做处理 +// StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); +// if (streamPushItem != null) { +// // TODO 发送停止 +// +// } + } + return ret; + } + + /** + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") + public DeferredResult onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) { + logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + + DeferredResult defaultResult = new DeferredResult<>(); + + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + if (!userSetting.isAutoApplyPlay() || mediaInfo == null) { + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); + return defaultResult; + } + + if ("rtp".equals(param.getApp())) { + String[] s = param.getStream().split("_"); + if (!mediaInfo.isRtpEnable() || s.length != 2) { + defaultResult.setResult(HookResult.SUCCESS()); + return defaultResult; + } + String deviceId = s[0]; + String channelId = s[1]; + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null) { + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); + return defaultResult; + } + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel == null) { + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); + return defaultResult; + } + logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + RequestMessage msg = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + boolean exist = resultHolder.exist(key, null); + msg.setKey(key); + String uuid = UUID.randomUUID().toString(); + msg.setId(uuid); + DeferredResult result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + DeferredResultEx deferredResultEx = new DeferredResultEx<>(result); + + result.onTimeout(() -> { + logger.info("点播接口等待超时"); + // 释放rtpserver + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时")); + resultHolder.invokeResult(msg); + }); + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误 + deferredResultEx.setFilter(result1 -> { + WVPResult wvpResult1 = (WVPResult) result1; + HookResult resultForEnd = new HookResult(); + resultForEnd.setCode(wvpResult1.getCode()); + resultForEnd.setMsg(wvpResult1.getMsg()); + return resultForEnd; + }); + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, deferredResultEx); + + if (!exist) { + playService.play(mediaInfo, deviceId, channelId, null, eventResult -> { + msg.setData(new HookResult(eventResult.statusCode, eventResult.msg)); + resultHolder.invokeResult(msg); + }, null); + } + return result; + } else { + // 拉流代理 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) { + streamProxyService.start(param.getApp(), param.getStream()); + } + DeferredResult result = new DeferredResult<>(); + result.setResult(HookResult.SUCCESS()); + return result; + } + } + + /** + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") + public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) { + + jsonObject.put("ip", request.getRemoteAddr()); + ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject); + zlmServerConfig.setIp(request.getRemoteAddr()); + logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId()); + taskExecutor.execute(() -> { + List subscribes = this.subscribe.getSubscribes(HookType.on_server_started); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, jsonObject); + } + } + mediaServerService.zlmServerOnline(zlmServerConfig); + }); + + return HookResult.SUCCESS(); + } + + /** + * 发送rtp(startSendRtp)被动关闭时回调 + */ + @ResponseBody + @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8") + public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) { + + logger.info("[ZLM HOOK] rtp发送关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream()); + + // 查找对应的上级推流,发送停止 + if (!"rtp".equals(param.getApp())) { + return HookResult.SUCCESS(); + } + taskExecutor.execute(() -> { + List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + try { + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), + sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + } + } + }); + + return HookResult.SUCCESS(); + } + + /** + * rtpServer收流超时 + */ + @ResponseBody + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8") + public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) { + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc()); + + taskExecutor.execute(() -> { + JSONObject json = (JSONObject) JSON.toJSON(param); + List subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, json); + } + } + }); + + return HookResult.SUCCESS(); + } + + private Map urlParamToMap(String params) { + HashMap map = new HashMap<>(); + if (ObjectUtils.isEmpty(params)) { + return map; + } + String[] paramsArray = params.split("&"); + if (paramsArray.length == 0) { + return map; + } + for (String param : paramsArray) { + String[] paramArray = param.split("="); + if (paramArray.length == 2) { + map.put(paramArray[0], paramArray[1]); + } + } + return map; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMMediaListManager.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMMediaListManager.java new file mode 100644 index 0000000..657c1e3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMMediaListManager.java @@ -0,0 +1,135 @@ +package com.yfd.monitor.media.zlm; + +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.media.zlm.dto.*; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.GbStreamMapper; +import com.yfd.monitor.storager.dao.PlatformGbStreamMapper; +import com.yfd.monitor.storager.dao.StreamPushMapper; +import com.yfd.monitor.utils.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +@Component +public class ZLMMediaListManager { + + private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager"); + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + private Map channelOnPublishEvents = new ConcurrentHashMap<>(); + + public StreamPushItem addPush(OnStreamChangedHookParam onStreamChangedHookParam) { + StreamPushItem transform = streamPushService.transform(onStreamChangedHookParam); + StreamPushItem pushInDb = streamPushService.getPush(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); + transform.setPushIng(onStreamChangedHookParam.isRegist()); + transform.setUpdateTime(DateUtil.getNow()); + transform.setPushTime(DateUtil.getNow()); + transform.setSelf(userSetting.getServerId().equals(onStreamChangedHookParam.getSeverId())); + if (pushInDb == null) { + transform.setCreateTime(DateUtil.getNow()); + streamPushMapper.add(transform); + }else { + streamPushMapper.update(transform); + gbStreamMapper.updateMediaServer(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), onStreamChangedHookParam.getMediaServerId()); + } + ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream()); + if ( channelOnlineEventLister != null) { + try { + channelOnlineEventLister.run(transform.getApp(), transform.getStream(), transform.getServerId()); + } catch (ParseException e) { + logger.error("addPush: ", e); + } + removedChannelOnlineEventLister(transform.getApp(), transform.getStream()); + } + return transform; + } + + public void sendStreamEvent(String app, String stream, String mediaServerId) { + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + // 查看推流状态 + if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) { + ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream); + if (channelOnlineEventLister != null) { + try { + channelOnlineEventLister.run(app, stream, mediaServerId); + } catch (ParseException e) { + logger.error("sendStreamEvent: ", e); + } + removedChannelOnlineEventLister(app, stream); + } + } + } + + public int removeMedia(String app, String streamId) { + // 查找是否关联了国标, 关联了不删除, 置为离线 + GbStream gbStream = gbStreamMapper.selectOne(app, streamId); + int result; + if (gbStream == null) { + result = storager.removeMedia(app, streamId); + }else { + result =storager.mediaOffline(app, streamId); + } + return result; + } + + public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) { + this.channelOnPublishEvents.put(app + "_" + stream, callback); + } + + public void removedChannelOnlineEventLister(String app, String stream) { + this.channelOnPublishEvents.remove(app + "_" + stream); + } + + public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) { + return this.channelOnPublishEvents.get(app + "_" + stream); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRESTfulUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRESTfulUtils.java new file mode 100644 index 0000000..da623c0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRESTfulUtils.java @@ -0,0 +1,398 @@ +package com.yfd.monitor.media.zlm; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import okhttp3.*; +import okhttp3.logging.HttpLoggingInterceptor; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@Component +public class ZLMRESTfulUtils { + + private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); + + private OkHttpClient client; + + public interface RequestCallback{ + void run(JSONObject response); + } + + private OkHttpClient getClient(){ + if (client == null) { + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); + //todo 暂时写死超时时间 均为5s + // 设置连接超时时间 + httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); + // 设置读取超时时间 + httpClientBuilder.readTimeout(10,TimeUnit.SECONDS); + // 设置连接池 + httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); + if (logger.isDebugEnabled()) { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { + logger.debug("http请求参数:" + message); + }); + logging.setLevel(HttpLoggingInterceptor.Level.BASIC); + // OkHttp進行添加攔截器loggingInterceptor + httpClientBuilder.addInterceptor(logging); + } + client = httpClientBuilder.build(); + } + return client; + + } + + + public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map param, RequestCallback callback) { + OkHttpClient client = getClient(); + + if (mediaServerItem == null) { + return null; + } + String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); + System.out.println(url); + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code",-2); + responseJSON.put("msg","流媒体调用失败"); + + FormBody.Builder builder = new FormBody.Builder(); + builder.add("secret",mediaServerItem.getSecret()); + if (param != null && param.keySet().size() > 0) { + for (String key : param.keySet()){ + if (param.get(key) != null) { + builder.add(key, param.get(key).toString()); + } + } + } + + FormBody body = builder.build(); + + Request request = new Request.Builder() + .post(body) + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON = JSON.parseObject(responseStr); + } + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + }catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + + if(e instanceof SocketTimeoutException){ + //读取超时超时异常 + logger.error(String.format("读取ZLM数据失败: %s, %s", url, e.getMessage())); + } + if(e instanceof ConnectException){ + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接ZLM失败: %s, %s", url, e.getMessage())); + } + + }catch (Exception e){ + logger.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage())); + } + }else { + client.newCall(request).enqueue(new Callback(){ + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response){ + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + + if(e instanceof SocketTimeoutException){ + //读取超时超时异常 + logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if(e instanceof ConnectException){ + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + } + } + }); + } + + + + return responseJSON; + } + + public void sendGetForImg(MediaServerItem mediaServerItem, String api, Map params, String targetPath, String fileName) throws IOException { + String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); + logger.debug(url); + HttpUrl parseUrl = HttpUrl.parse(url); + if (parseUrl == null) { + return; + } + HttpUrl.Builder httpBuilder = parseUrl.newBuilder(); + + httpBuilder.addQueryParameter("secret", mediaServerItem.getSecret()); + if (params != null) { + for (Map.Entry param : params.entrySet()) { + httpBuilder.addQueryParameter(param.getKey(), param.getValue().toString()); + } + } + + Request request = new Request.Builder() + .url(httpBuilder.build()) + .build(); + logger.info(request.toString()); + FileOutputStream outStream = null; + try { + OkHttpClient client = getClient(); + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + if (targetPath != null) { + File snapFolder = new File(targetPath); + if (!snapFolder.exists()) { + if (!snapFolder.mkdirs()) { + logger.warn("{}路径创建失败", snapFolder.getAbsolutePath()); + } + + } + File snapFile = new File(targetPath + File.separator + fileName); + outStream = new FileOutputStream(snapFile); + + outStream.write(Objects.requireNonNull(response.body()).bytes()); + + } else { + logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + } + Objects.requireNonNull(response.body()).close(); + } else { + logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + } + } catch (ConnectException e) { + logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + logger.info("请检查media配置并确认ZLM已启动..."); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } finally { + if (outStream!=null) { + outStream.flush(); + outStream.close(); + } + } + } + + public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){ + Map param = new HashMap<>(); + if (app != null) { + param.put("app",app); + } + if (stream != null) { + param.put("stream",stream); + } + if (schema != null) { + param.put("schema",schema); + } + param.put("vhost","__defaultVhost__"); + return sendPost(mediaServerItem, "getMediaList",param, callback); + } + + public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){ + return getMediaList(mediaServerItem, app, stream,null, null); + } + + public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){ + return sendPost(mediaServerItem, "getMediaList",null, callback); + } + + public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){ + Map param = new HashMap<>(); + param.put("app",app); + param.put("schema",schema); + param.put("stream",stream); + param.put("vhost","__defaultVhost__"); + return sendPost(mediaServerItem, "getMediaInfo",param, null); + } + + public JSONObject getRtpInfo(MediaServerItem mediaServerItem, String stream_id){ + Map param = new HashMap<>(); + param.put("stream_id",stream_id); + return sendPost(mediaServerItem, "getRtpInfo",param, null); + } + + public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms, + boolean enable_audio, boolean enable_mp4, String ffmpeg_cmd_key){ + logger.info(src_url); + logger.info(dst_url); + Map param = new HashMap<>(); + param.put("src_url", src_url); + param.put("dst_url", dst_url); + param.put("timeout_ms", timeout_ms); + param.put("enable_mp4", enable_mp4); + param.put("ffmpeg_cmd_key", ffmpeg_cmd_key); + return sendPost(mediaServerItem, "addFFmpegSource",param, null); + } + + public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){ + Map param = new HashMap<>(); + param.put("key", key); + return sendPost(mediaServerItem, "delFFmpegSource",param, null); + } + + public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){ + return sendPost(mediaServerItem, "getServerConfig",null, null); + } + + public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map param){ + return sendPost(mediaServerItem,"setServerConfig",param, null); + } + + public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map param){ + return sendPost(mediaServerItem, "openRtpServer",param, null); + } + + public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "closeRtpServer",param, null); + } + + public void closeRtpServer(MediaServerItem mediaServerItem, Map param, RequestCallback callback) { + sendPost(mediaServerItem, "closeRtpServer",param, callback); + } + + public JSONObject listRtpServer(MediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "listRtpServer",null, null); + } + + public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "startSendRtp",param, null); + } + + public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "stopSendRtp",param, null); + } + + public JSONObject restartServer(MediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "restartServer",null, null); + } + + public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) { + Map param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + param.put("url", url); + param.put("enable_mp4", enable_mp4?1:0); + param.put("enable_audio", enable_audio?1:0); + param.put("rtp_type", rtp_type); + return sendPost(mediaServerItem, "addStreamProxy",param, null); + } + + + public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) { + Map param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + param.put("force", 1); + return sendPost(mediaServerItem, "close_streams",param, null); + } + + public JSONObject getAllSession(MediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "getAllSession",null, null); + } + + public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) { + Map param = new HashMap<>(); + param.put("local_port", localPortSStr); + sendPost(mediaServerItem, "kick_sessions",param, null); + } + + public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) throws IOException { + Map param = new HashMap<>(3); + param.put("url", streamUrl); + param.put("timeout_sec", timeout_sec); + param.put("expire_sec", expire_sec); + sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName); + } + + public JSONObject pauseRtpCheck(MediaServerItem mediaServerItem, String streamId) { + Map param = new HashMap<>(1); + param.put("stream_id", streamId); + return sendPost(mediaServerItem, "pauseRtpCheck",param, null); + } + + public JSONObject resumeRtpCheck(MediaServerItem mediaServerItem, String streamId) { + Map param = new HashMap<>(1); + param.put("stream_id", streamId); + return sendPost(mediaServerItem, "resumeRtpCheck",param, null); + } + + public JSONObject connectRtpServer(MediaServerItem mediaServerItem, String dst_url, int dst_port, String stream_id) { + Map param = new HashMap<>(1); + param.put("dst_url", dst_url); + param.put("dst_port", dst_port); + param.put("stream_id", stream_id); + return sendPost(mediaServerItem, "connectRtpServer",param, null); + } + + public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map param) { + return sendPost(mediaServerItem, "startSendRtpPassive",param, null); + } + + public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Map param, RequestCallback callback) { + return sendPost(mediaServerItem, "startSendRtpPassive",param, callback); + } + + public JSONObject updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) { + Map param = new HashMap<>(1); + param.put("ssrc", ssrc); + param.put("stream_id", streamId); + return sendPost(mediaServerItem, "updateRtpServerSSRC",param, null); + } + + public JSONObject deleteRecordDirectory(MediaServerItem mediaServerItem, String app, String stream, String date, String fileName) { + Map param = new HashMap<>(1); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + param.put("period", date); + param.put("name", fileName); + return sendPost(mediaServerItem, "deleteRecordDirectory",param, null); + } + + public JSONObject delStreamProxy(MediaServerItem mediaServerItem, String key){ + Map param = new HashMap<>(); + param.put("key", key); + return sendPost(mediaServerItem, "delStreamProxy",param, null); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRTPServerFactory.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRTPServerFactory.java new file mode 100644 index 0000000..7e60251 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRTPServerFactory.java @@ -0,0 +1,388 @@ +package com.yfd.monitor.media.zlm; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.zlm.dto.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; + +@Component +public class ZLMRTPServerFactory { + + private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory"); + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + private int[] portRangeArray = new int[2]; + + public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List usedFreelist) { + if (endPort <= startPort) { + return -1; + } + if (usedFreelist == null) { + usedFreelist = new ArrayList<>(); + } + JSONObject listRtpServerJsonResult = zlmresTfulUtils.listRtpServer(mediaServerItem); + if (listRtpServerJsonResult != null) { + JSONArray data = listRtpServerJsonResult.getJSONArray("data"); + if (data != null) { + for (int i = 0; i < data.size(); i++) { + JSONObject dataItem = data.getJSONObject(i); + usedFreelist.add(dataItem.getInteger("port")); + } + } + } + + Map param = new HashMap<>(); + int result = -1; + // 设置推流端口 + if (startPort%2 == 1) { + startPort ++; + } + boolean checkPort = false; + for (int i = startPort; i < endPort + 1; i+=2) { + if (!usedFreelist.contains(i)){ + checkPort = true; + startPort = i; + break; + } + } + if (!checkPort) { + logger.warn("未找到节点{}上范围[{}-{}]的空闲端口", mediaServerItem.getId(), startPort, endPort); + return -1; + } + param.put("port", startPort); + String stream = UUID.randomUUID().toString(); + param.put("enable_tcp", 1); + param.put("stream_id", stream); +// param.put("port", 0); + JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); + + if (openRtpServerResultJson != null) { + if (openRtpServerResultJson.getInteger("code") == 0) { + result= openRtpServerResultJson.getInteger("port"); + Map closeRtpServerParam = new HashMap<>(); + closeRtpServerParam.put("stream_id", stream); + zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam); + }else { + usedFreelist.add(startPort); + startPort +=2; + result = getFreePort(mediaServerItem, startPort, endPort,usedFreelist); + } + }else { + // 检查ZLM状态 + logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port")); + } + return result; + } + + /** + * 开启rtpServer + * @param mediaServerItem zlm服务实例 + * @param streamId 流Id + * @param ssrc ssrc + * @param port 端口, 0/null为使用随机 + * @param reUsePort 是否重用端口 + * @param tcpMode 0/null udp 模式,1 tcp 被动模式, 2 tcp 主动模式。 + * @return + */ + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port, Boolean reUsePort, Integer tcpMode) { + int result = -1; + // 查询此rtp server 是否已经存在 + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId); + logger.info(JSONObject.toJSONString(rtpInfo)); + if(rtpInfo.getInteger("code") == 0){ + if (rtpInfo.getBoolean("exist")) { + result = rtpInfo.getInteger("local_port"); + if (result == 0) { + // 此时说明rtpServer已经创建但是流还没有推上来 + // 此时重新打开rtpServer + Map param = new HashMap<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerItem, param); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + return createRTPServer(mediaServerItem, streamId, ssrc, port, reUsePort, tcpMode); + }else { + logger.warn("[开启rtpServer], 重启RtpServer错误"); + } + } + } + return result; + } + }else if(rtpInfo.getInteger("code") == -2){ + return result; + } + + Map param = new HashMap<>(); + + if (tcpMode == null) { + tcpMode = 0; + } + param.put("tcp_mode", tcpMode); + param.put("stream_id", streamId); + if (reUsePort != null) { + param.put("re_use_port", reUsePort?"1":"0"); + } + // 推流端口设置0则使用随机端口 + if (port == null) { + param.put("port", 0); + }else { + param.put("port", port); + } + param.put("ssrc", ssrc); + JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); + logger.info(JSONObject.toJSONString(openRtpServerResultJson)); + if (openRtpServerResultJson != null) { + if (openRtpServerResultJson.getInteger("code") == 0) { + result= openRtpServerResultJson.getInteger("port"); + }else { + logger.error("创建RTP Server 失败 {}: ", openRtpServerResultJson.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port")); + } + return result; + } + + public boolean closeRtpServer(MediaServerItem serverItem, String streamId) { + boolean result = false; + if (serverItem !=null){ + Map param = new HashMap<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + result = jsonObject.getInteger("hit") == 1; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + } + } + return result; + } + + public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback callback) { + if (serverItem == null) { + callback.run(false); + return; + } + Map param = new HashMap<>(); + param.put("stream_id", streamId); + zlmresTfulUtils.closeRtpServer(serverItem, param, jsonObject -> { + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + callback.run(jsonObject.getInteger("hit") == 1); + return; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + } + callback.run(false); + }); + + + } + + + /** + * 创建一个国标推流 + * @param ip 推流ip + * @param port 推流端口 + * @param ssrc 推流唯一标识 + * @param platformId 平台id + * @param channelId 通道id + * @param tcp 是否为tcp + * @return SendRtpItem + */ + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp, boolean rtcp){ + + // 默认为随机端口 + int localPort = 0; + if (userSetting.getGbSendStreamStrict()) { + if (userSetting.getGbSendStreamStrict()) { + localPort = keepPort(serverItem, ssrc); + if (localPort == 0) { + return null; + } + } + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setPlatformId(platformId); + sendRtpItem.setDeviceId(deviceId); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(tcp); + sendRtpItem.setRtcp(rtcp); + sendRtpItem.setApp("rtp"); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); + sendRtpItem.setMediaServerId(serverItem.getId()); + return sendRtpItem; + } + + /** + * 创建一个直播推流 + * @param ip 推流ip + * @param port 推流端口 + * @param ssrc 推流唯一标识 + * @param platformId 平台id + * @param channelId 通道id + * @param tcp 是否为tcp + * @return SendRtpItem + */ + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp, boolean rtcp){ + // 默认为随机端口 + int localPort = 0; + if (userSetting.getGbSendStreamStrict()) { + localPort = keepPort(serverItem, ssrc); + if (localPort == 0) { + return null; + } + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setApp(app); + sendRtpItem.setStreamId(stream); + sendRtpItem.setPlatformId(platformId); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(tcp); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); + sendRtpItem.setMediaServerId(serverItem.getId()); + sendRtpItem.setRtcp(rtcp); + return sendRtpItem; + } + + /** + * 保持端口,直到需要需要发流时再释放 + */ + public int keepPort(MediaServerItem serverItem, String ssrc) { + int localPort = 0; + Map param = new HashMap<>(3); + param.put("port", 0); + param.put("enable_tcp", 1); + param.put("stream_id", ssrc); + JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param); + if (jsonObject.getInteger("code") == 0) { + localPort = jsonObject.getInteger("port"); + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, + (MediaServerItem mediaServerItem, JSONObject response)->{ + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc); + keepPort(serverItem, ssrc); + }); + logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort); + }else { + logger.info("[上级点播] 监听端口失败: {}", ssrc); + } + return localPort; + } + + /** + * 释放保持的端口 + */ + public boolean releasePort(MediaServerItem serverItem, String ssrc) { + logger.error("[上级点播] {}->释放监听端口", ssrc); + boolean closeRTPServerResult = closeRtpServer(serverItem, ssrc); + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout); + return closeRTPServerResult; + } + + /** + * 调用zlm RESTFUL API —— startSendRtp + */ + public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { + return zlmresTfulUtils.startSendRtp(mediaServerItem, param); + } + + /** + * 查询待转推的流是否就绪 + */ + public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtsp", streamId); + return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); + } + + /** + * 查询待转推的流是否就绪 + */ + public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId); + return mediaInfo != null && (mediaInfo.getInteger("code") == 0 + + && mediaInfo.getJSONArray("data") != null + && mediaInfo.getJSONArray("data").size() > 0); + } + + /** + * 查询转推的流是否有其它观看者 + * @param streamId + * @return + */ + public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId); + if (mediaInfo == null) { + return 0; + } + Integer code = mediaInfo.getInteger("code"); + if ( code < 0) { + logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); + return -1; + } + if ( code == 0 && mediaInfo.getBoolean("online") != null && !mediaInfo.getBoolean("online")) { + logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); + return -1; + } + return mediaInfo.getInteger("totalReaderCount"); + } + + /** + * 调用zlm RESTful API —— stopSendRtp + */ + public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { + Boolean result = false; + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); + if (jsonObject == null) { + logger.error("[停止RTP推流] 失败: 请检查ZLM服务"); + } else if (jsonObject.getInteger("code") == 0) { + result= true; + logger.info("[停止RTP推流] 成功"); + } else { + logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject); + } + return result; + } + + public void closeAllSendRtpStream() { + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRunner.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRunner.java new file mode 100644 index 0000000..0f3c033 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMRunner.java @@ -0,0 +1,172 @@ +package com.yfd.monitor.media.zlm; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.MediaConfig; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.media.zlm.dto.HookSubscribeFactory; +import com.yfd.monitor.media.zlm.dto.HookSubscribeForServerStarted; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@Component +@Order(value=12) +public class ZLMRunner implements CommandLineRunner { + + private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); + + private Map startGetMedia; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + + @Autowired + private EventPublisher publisher; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private MediaConfig mediaConfig; + + @Autowired + private DynamicTask dynamicTask; + + + @Override + public void run(String... strings) throws Exception { + mediaServerService.clearMediaServerForOnline(); + MediaServerItem defaultMediaServer = mediaServerService.getDefaultMediaServer(); + if (defaultMediaServer == null) { + mediaServerService.addToDatabase(mediaConfig.getMediaSerItem()); + }else { + MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem(); + mediaServerService.updateToDatabase(mediaSerItem); + } + mediaServerService.syncCatchFromDatabase(); + HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started(); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.addSubscribe(hookSubscribeForServerStarted, + (MediaServerItem mediaServerItem, JSONObject response)->{ + ZLMServerConfig zlmServerConfig = response.to(ZLMServerConfig.class); + if (zlmServerConfig !=null ) { + if (startGetMedia != null) { + startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } + } + } + }); + + // 获取zlm信息 + logger.info("[zlm] 等待默认zlm中..."); + + // 获取所有的zlm, 并开启主动连接 + List all = mediaServerService.getAllFromDatabase(); + Map allMap = new HashMap<>(); + mediaServerService.updateVmServer(all); + if (all.size() == 0) { + all.add(mediaConfig.getMediaSerItem()); + } + for (MediaServerItem mediaServerItem : all) { + if (startGetMedia == null) { + startGetMedia = new ConcurrentHashMap<>(); + } + startGetMedia.put(mediaServerItem.getId(), true); + connectZlmServer(mediaServerItem); + allMap.put(mediaServerItem.getId(), mediaServerItem); + } + String taskKey = "zlm-connect-timeout"; + dynamicTask.startDelay(taskKey, ()->{ + if (startGetMedia != null && startGetMedia.size() > 0) { + Set allZlmId = startGetMedia.keySet(); + for (String id : allZlmId) { + logger.error("[ {} ]]主动连接失败,不再尝试连接", id); + } + startGetMedia = null; + } + // 获取redis中所有的zlm + List allInRedis = mediaServerService.getAll(); + for (MediaServerItem mediaServerItem : allInRedis) { + if (!allMap.containsKey(mediaServerItem.getId())) { + mediaServerService.delete(mediaServerItem.getId()); + } + } + }, 60 * 1000 ); + } + + @Async("taskExecutor") + public void connectZlmServer(MediaServerItem mediaServerItem){ + String connectZlmServerTaskKey = "connect-zlm-" + mediaServerItem.getId(); + ZLMServerConfig zlmServerConfigFirst = getMediaServerConfig(mediaServerItem); + if (zlmServerConfigFirst != null) { + zlmServerConfigFirst.setIp(mediaServerItem.getIp()); + zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort()); + startGetMedia.remove(mediaServerItem.getId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } + mediaServerService.zlmServerOnline(zlmServerConfigFirst); + }else { + logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + publisher.zlmOfflineEventPublish(mediaServerItem.getId()); + } + + dynamicTask.startCron(connectZlmServerTaskKey, ()->{ + ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem); + if (zlmServerConfig != null) { + dynamicTask.stop(connectZlmServerTaskKey); + zlmServerConfig.setIp(mediaServerItem.getIp()); + zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort()); + startGetMedia.remove(mediaServerItem.getId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } + mediaServerService.zlmServerOnline(zlmServerConfig); + } + }, 2000); + } + + public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) { + if (startGetMedia == null) { return null;} + if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) { + return null; + } + if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) { + return null; + } + JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + ZLMServerConfig zlmServerConfig = null; + if (responseJson != null) { + JSONArray data = responseJson.getJSONArray("data"); + if (data != null && data.size() > 0) { + zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); + } + } else { + logger.error("[ {} ]-[ {}:{} ]主动连接失败, 2s后重试", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + } + return zlmServerConfig; + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMServerConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMServerConfig.java new file mode 100644 index 0000000..d083674 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZLMServerConfig.java @@ -0,0 +1,1220 @@ +package com.yfd.monitor.media.zlm; + +import com.alibaba.fastjson2.annotation.JSONField; + +public class ZLMServerConfig { + + @JSONField(name = "api.apiDebug") + private String apiDebug; + + @JSONField(name = "api.secret") + private String apiSecret; + + @JSONField(name = "api.snapRoot") + private String apiSnapRoot; + + @JSONField(name = "api.defaultSnap") + private String apiDefaultSnap; + + @JSONField(name = "ffmpeg.bin") + private String ffmpegBin; + + @JSONField(name = "ffmpeg.cmd") + private String ffmpegCmd; + + @JSONField(name = "ffmpeg.snap") + private String ffmpegSnap; + + @JSONField(name = "ffmpeg.log") + private String ffmpegLog; + + @JSONField(name = "ffmpeg.restart_sec") + private String ffmpegRestartSec; + + @JSONField(name = "protocol.modify_stamp") + private String protocolModifyStamp; + + @JSONField(name = "protocol.enable_audio") + private String protocolEnableAudio; + + @JSONField(name = "protocol.add_mute_audio") + private String protocolAddMuteAudio; + + @JSONField(name = "protocol.continue_push_ms") + private String protocolContinuePushMs; + + @JSONField(name = "protocol.enable_hls") + private String protocolEnableHls; + + @JSONField(name = "protocol.enable_mp4") + private String protocolEnableMp4; + + @JSONField(name = "protocol.enable_rtsp") + private String protocolEnableRtsp; + + @JSONField(name = "protocol.enable_rtmp") + private String protocolEnableRtmp; + + @JSONField(name = "protocol.enable_ts") + private String protocolEnableTs; + + @JSONField(name = "protocol.enable_fmp4") + private String protocolEnableFmp4; + + @JSONField(name = "protocol.mp4_as_player") + private String protocolMp4AsPlayer; + + @JSONField(name = "protocol.mp4_max_second") + private String protocolMp4MaxSecond; + + @JSONField(name = "protocol.mp4_save_path") + private String protocolMp4SavePath; + + @JSONField(name = "protocol.hls_save_path") + private String protocolHlsSavePath; + + @JSONField(name = "protocol.hls_demand") + private String protocolHlsDemand; + + @JSONField(name = "protocol.rtsp_demand") + private String protocolRtspDemand; + + @JSONField(name = "protocol.rtmp_demand") + private String protocolRtmpDemand; + + @JSONField(name = "protocol.ts_demand") + private String protocolTsDemand; + + @JSONField(name = "protocol.fmp4_demand") + private String protocolFmp4Demand; + + @JSONField(name = "general.enableVhost") + private String generalEnableVhost; + + @JSONField(name = "general.flowThreshold") + private String generalFlowThreshold; + + @JSONField(name = "general.maxStreamWaitMS") + private String generalMaxStreamWaitMS; + + @JSONField(name = "general.streamNoneReaderDelayMS") + private int generalStreamNoneReaderDelayMS; + + @JSONField(name = "general.resetWhenRePlay") + private String generalResetWhenRePlay; + + @JSONField(name = "general.mergeWriteMS") + private String generalMergeWriteMS; + + @JSONField(name = "general.mediaServerId") + private String generalMediaServerId; + + @JSONField(name = "general.wait_track_ready_ms") + private String generalWaitTrackReadyMs; + + @JSONField(name = "general.wait_add_track_ms") + private String generalWaitAddTrackMs; + + @JSONField(name = "general.unready_frame_cache") + private String generalUnreadyFrameCache; + + + @JSONField(name = "ip") + private String ip; + + private String sdpIp; + + private String streamIp; + + private String hookIp; + + private String updateTime; + + private String createTime; + + @JSONField(name = "hls.fileBufSize") + private String hlsFileBufSize; + + @JSONField(name = "hls.filePath") + private String hlsFilePath; + + @JSONField(name = "hls.segDur") + private String hlsSegDur; + + @JSONField(name = "hls.segNum") + private String hlsSegNum; + + @JSONField(name = "hls.segRetain") + private String hlsSegRetain; + + @JSONField(name = "hls.broadcastRecordTs") + private String hlsBroadcastRecordTs; + + @JSONField(name = "hls.deleteDelaySec") + private String hlsDeleteDelaySec; + + @JSONField(name = "hls.segKeep") + private String hlsSegKeep; + + @JSONField(name = "hook.access_file_except_hls") + private String hookAccessFileExceptHLS; + + @JSONField(name = "hook.admin_params") + private String hookAdminParams; + + @JSONField(name = "hook.alive_interval") + private Float hookAliveInterval; + + @JSONField(name = "hook.enable") + private String hookEnable; + + @JSONField(name = "hook.on_flow_report") + private String hookOnFlowReport; + + @JSONField(name = "hook.on_http_access") + private String hookOnHttpAccess; + + @JSONField(name = "hook.on_play") + private String hookOnPlay; + + @JSONField(name = "hook.on_publish") + private String hookOnPublish; + + @JSONField(name = "hook.on_record_mp4") + private String hookOnRecordMp4; + + @JSONField(name = "hook.on_rtsp_auth") + private String hookOnRtspAuth; + + @JSONField(name = "hook.on_rtsp_realm") + private String hookOnRtspRealm; + + @JSONField(name = "hook.on_shell_login") + private String hookOnShellLogin; + + @JSONField(name = "hook.on_stream_changed") + private String hookOnStreamChanged; + + @JSONField(name = "hook.on_stream_none_reader") + private String hookOnStreamNoneReader; + + @JSONField(name = "hook.on_stream_not_found") + private String hookOnStreamNotFound; + + @JSONField(name = "hook.on_server_started") + private String hookOnServerStarted; + + @JSONField(name = "hook.on_server_keepalive") + private String hookOnServerKeepalive; + + @JSONField(name = "hook.on_send_rtp_stopped") + private String hookOnSendRtpStopped; + + @JSONField(name = "hook.on_rtp_server_timeout") + private String hookOnRtpServerTimeout; + + @JSONField(name = "hook.timeoutSec") + private String hookTimeoutSec; + + @JSONField(name = "http.charSet") + private String httpCharSet; + + @JSONField(name = "http.keepAliveSecond") + private String httpKeepAliveSecond; + + @JSONField(name = "http.maxReqCount") + private String httpMaxReqCount; + + @JSONField(name = "http.maxReqSize") + private String httpMaxReqSize; + + @JSONField(name = "http.notFound") + private String httpNotFound; + + @JSONField(name = "http.port") + private int httpPort; + + @JSONField(name = "http.rootPath") + private String httpRootPath; + + @JSONField(name = "http.sendBufSize") + private String httpSendBufSize; + + @JSONField(name = "http.sslport") + private int httpSSLport; + + @JSONField(name = "multicast.addrMax") + private String multicastAddrMax; + + @JSONField(name = "multicast.addrMin") + private String multicastAddrMin; + + @JSONField(name = "multicast.udpTTL") + private String multicastUdpTTL; + + @JSONField(name = "record.appName") + private String recordAppName; + + @JSONField(name = "record.filePath") + private String recordFilePath; + + @JSONField(name = "record.fileSecond") + private String recordFileSecond; + + @JSONField(name = "record.sampleMS") + private String recordFileSampleMS; + + @JSONField(name = "rtmp.handshakeSecond") + private String rtmpHandshakeSecond; + + @JSONField(name = "rtmp.keepAliveSecond") + private String rtmpKeepAliveSecond; + + @JSONField(name = "rtmp.modifyStamp") + private String rtmpModifyStamp; + + @JSONField(name = "rtmp.port") + private int rtmpPort; + + @JSONField(name = "rtmp.sslport") + private int rtmpSslPort; + + @JSONField(name = "rtp.audioMtuSize") + private String rtpAudioMtuSize; + + @JSONField(name = "rtp.clearCount") + private String rtpClearCount; + + @JSONField(name = "rtp.cycleMS") + private String rtpCycleMS; + + @JSONField(name = "rtp.maxRtpCount") + private String rtpMaxRtpCount; + + @JSONField(name = "rtp.videoMtuSize") + private String rtpVideoMtuSize; + + @JSONField(name = "rtp_proxy.checkSource") + private String rtpProxyCheckSource; + + @JSONField(name = "rtp_proxy.dumpDir") + private String rtpProxyDumpDir; + + @JSONField(name = "rtp_proxy.port") + private int rtpProxyPort; + + @JSONField(name = "rtp_proxy.port_range") + private String portRange; + + @JSONField(name = "rtp_proxy.timeoutSec") + private String rtpProxyTimeoutSec; + + @JSONField(name = "rtsp.authBasic") + private String rtspAuthBasic; + + @JSONField(name = "rtsp.handshakeSecond") + private String rtspHandshakeSecond; + + @JSONField(name = "rtsp.keepAliveSecond") + private String rtspKeepAliveSecond; + + @JSONField(name = "rtsp.port") + private int rtspPort; + + @JSONField(name = "rtsp.sslport") + private int rtspSSlport; + + @JSONField(name = "shell.maxReqSize") + private String shellMaxReqSize; + + @JSONField(name = "shell.shell") + private String shellPhell; + + @JSONField(name = "transcode.suffix") + private String transcodeSuffix; + + + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + + public String getApiDebug() { + return apiDebug; + } + + public void setApiDebug(String apiDebug) { + this.apiDebug = apiDebug; + } + + public String getApiSecret() { + return apiSecret; + } + + public void setApiSecret(String apiSecret) { + this.apiSecret = apiSecret; + } + + public String getFfmpegBin() { + return ffmpegBin; + } + + public void setFfmpegBin(String ffmpegBin) { + this.ffmpegBin = ffmpegBin; + } + + public String getFfmpegCmd() { + return ffmpegCmd; + } + + public void setFfmpegCmd(String ffmpegCmd) { + this.ffmpegCmd = ffmpegCmd; + } + + public String getFfmpegLog() { + return ffmpegLog; + } + + public void setFfmpegLog(String ffmpegLog) { + this.ffmpegLog = ffmpegLog; + } + + public String getGeneralEnableVhost() { + return generalEnableVhost; + } + + public void setGeneralEnableVhost(String generalEnableVhost) { + this.generalEnableVhost = generalEnableVhost; + } + + public String getGeneralMediaServerId() { + return generalMediaServerId; + } + + public void setGeneralMediaServerId(String generalMediaServerId) { + this.generalMediaServerId = generalMediaServerId; + } + + public String getGeneralFlowThreshold() { + return generalFlowThreshold; + } + + public void setGeneralFlowThreshold(String generalFlowThreshold) { + this.generalFlowThreshold = generalFlowThreshold; + } + + public String getGeneralMaxStreamWaitMS() { + return generalMaxStreamWaitMS; + } + + public void setGeneralMaxStreamWaitMS(String generalMaxStreamWaitMS) { + this.generalMaxStreamWaitMS = generalMaxStreamWaitMS; + } + + public int getGeneralStreamNoneReaderDelayMS() { + return generalStreamNoneReaderDelayMS; + } + + public void setGeneralStreamNoneReaderDelayMS(int generalStreamNoneReaderDelayMS) { + this.generalStreamNoneReaderDelayMS = generalStreamNoneReaderDelayMS; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getStreamIp() { + return streamIp; + } + + public void setStreamIp(String streamIp) { + this.streamIp = streamIp; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getHlsFileBufSize() { + return hlsFileBufSize; + } + + public void setHlsFileBufSize(String hlsFileBufSize) { + this.hlsFileBufSize = hlsFileBufSize; + } + + public String getHlsFilePath() { + return hlsFilePath; + } + + public void setHlsFilePath(String hlsFilePath) { + this.hlsFilePath = hlsFilePath; + } + + public String getHlsSegDur() { + return hlsSegDur; + } + + public void setHlsSegDur(String hlsSegDur) { + this.hlsSegDur = hlsSegDur; + } + + public String getHlsSegNum() { + return hlsSegNum; + } + + public void setHlsSegNum(String hlsSegNum) { + this.hlsSegNum = hlsSegNum; + } + + public String getHookAccessFileExceptHLS() { + return hookAccessFileExceptHLS; + } + + public void setHookAccessFileExceptHLS(String hookAccessFileExceptHLS) { + this.hookAccessFileExceptHLS = hookAccessFileExceptHLS; + } + + public String getHookAdminParams() { + return hookAdminParams; + } + + public void setHookAdminParams(String hookAdminParams) { + this.hookAdminParams = hookAdminParams; + } + + public String getHookEnable() { + return hookEnable; + } + + public void setHookEnable(String hookEnable) { + this.hookEnable = hookEnable; + } + + public String getHookOnFlowReport() { + return hookOnFlowReport; + } + + public void setHookOnFlowReport(String hookOnFlowReport) { + this.hookOnFlowReport = hookOnFlowReport; + } + + public String getHookOnHttpAccess() { + return hookOnHttpAccess; + } + + public void setHookOnHttpAccess(String hookOnHttpAccess) { + this.hookOnHttpAccess = hookOnHttpAccess; + } + + public String getHookOnPlay() { + return hookOnPlay; + } + + public void setHookOnPlay(String hookOnPlay) { + this.hookOnPlay = hookOnPlay; + } + + public String getHookOnPublish() { + return hookOnPublish; + } + + public void setHookOnPublish(String hookOnPublish) { + this.hookOnPublish = hookOnPublish; + } + + public String getHookOnRecordMp4() { + return hookOnRecordMp4; + } + + public void setHookOnRecordMp4(String hookOnRecordMp4) { + this.hookOnRecordMp4 = hookOnRecordMp4; + } + + public String getHookOnRtspAuth() { + return hookOnRtspAuth; + } + + public void setHookOnRtspAuth(String hookOnRtspAuth) { + this.hookOnRtspAuth = hookOnRtspAuth; + } + + public String getHookOnRtspRealm() { + return hookOnRtspRealm; + } + + public void setHookOnRtspRealm(String hookOnRtspRealm) { + this.hookOnRtspRealm = hookOnRtspRealm; + } + + public String getHookOnShellLogin() { + return hookOnShellLogin; + } + + public void setHookOnShellLogin(String hookOnShellLogin) { + this.hookOnShellLogin = hookOnShellLogin; + } + + public String getHookOnStreamChanged() { + return hookOnStreamChanged; + } + + public void setHookOnStreamChanged(String hookOnStreamChanged) { + this.hookOnStreamChanged = hookOnStreamChanged; + } + + public String getHookOnStreamNoneReader() { + return hookOnStreamNoneReader; + } + + public void setHookOnStreamNoneReader(String hookOnStreamNoneReader) { + this.hookOnStreamNoneReader = hookOnStreamNoneReader; + } + + public String getHookOnStreamNotFound() { + return hookOnStreamNotFound; + } + + public void setHookOnStreamNotFound(String hookOnStreamNotFound) { + this.hookOnStreamNotFound = hookOnStreamNotFound; + } + + public String getHookTimeoutSec() { + return hookTimeoutSec; + } + + public void setHookTimeoutSec(String hookTimeoutSec) { + this.hookTimeoutSec = hookTimeoutSec; + } + + public String getHttpCharSet() { + return httpCharSet; + } + + public void setHttpCharSet(String httpCharSet) { + this.httpCharSet = httpCharSet; + } + + public String getHttpKeepAliveSecond() { + return httpKeepAliveSecond; + } + + public void setHttpKeepAliveSecond(String httpKeepAliveSecond) { + this.httpKeepAliveSecond = httpKeepAliveSecond; + } + + public String getHttpMaxReqCount() { + return httpMaxReqCount; + } + + public void setHttpMaxReqCount(String httpMaxReqCount) { + this.httpMaxReqCount = httpMaxReqCount; + } + + public String getHttpMaxReqSize() { + return httpMaxReqSize; + } + + public void setHttpMaxReqSize(String httpMaxReqSize) { + this.httpMaxReqSize = httpMaxReqSize; + } + + public String getHttpNotFound() { + return httpNotFound; + } + + public void setHttpNotFound(String httpNotFound) { + this.httpNotFound = httpNotFound; + } + + public int getHttpPort() { + return httpPort; + } + + public void setHttpPort(int httpPort) { + this.httpPort = httpPort; + } + + public String getHttpRootPath() { + return httpRootPath; + } + + public void setHttpRootPath(String httpRootPath) { + this.httpRootPath = httpRootPath; + } + + public String getHttpSendBufSize() { + return httpSendBufSize; + } + + public void setHttpSendBufSize(String httpSendBufSize) { + this.httpSendBufSize = httpSendBufSize; + } + + public int getHttpSSLport() { + return httpSSLport; + } + + public void setHttpSSLport(int httpSSLport) { + this.httpSSLport = httpSSLport; + } + + public String getMulticastAddrMax() { + return multicastAddrMax; + } + + public void setMulticastAddrMax(String multicastAddrMax) { + this.multicastAddrMax = multicastAddrMax; + } + + public String getMulticastAddrMin() { + return multicastAddrMin; + } + + public void setMulticastAddrMin(String multicastAddrMin) { + this.multicastAddrMin = multicastAddrMin; + } + + public String getMulticastUdpTTL() { + return multicastUdpTTL; + } + + public void setMulticastUdpTTL(String multicastUdpTTL) { + this.multicastUdpTTL = multicastUdpTTL; + } + + public String getRecordAppName() { + return recordAppName; + } + + public void setRecordAppName(String recordAppName) { + this.recordAppName = recordAppName; + } + + public String getRecordFilePath() { + return recordFilePath; + } + + public void setRecordFilePath(String recordFilePath) { + this.recordFilePath = recordFilePath; + } + + public String getRecordFileSecond() { + return recordFileSecond; + } + + public void setRecordFileSecond(String recordFileSecond) { + this.recordFileSecond = recordFileSecond; + } + + public String getRecordFileSampleMS() { + return recordFileSampleMS; + } + + public void setRecordFileSampleMS(String recordFileSampleMS) { + this.recordFileSampleMS = recordFileSampleMS; + } + + public String getRtmpHandshakeSecond() { + return rtmpHandshakeSecond; + } + + public void setRtmpHandshakeSecond(String rtmpHandshakeSecond) { + this.rtmpHandshakeSecond = rtmpHandshakeSecond; + } + + public String getRtmpKeepAliveSecond() { + return rtmpKeepAliveSecond; + } + + public void setRtmpKeepAliveSecond(String rtmpKeepAliveSecond) { + this.rtmpKeepAliveSecond = rtmpKeepAliveSecond; + } + + public String getRtmpModifyStamp() { + return rtmpModifyStamp; + } + + public void setRtmpModifyStamp(String rtmpModifyStamp) { + this.rtmpModifyStamp = rtmpModifyStamp; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public void setRtmpPort(int rtmpPort) { + this.rtmpPort = rtmpPort; + } + + public int getRtmpSslPort() { + return rtmpSslPort; + } + + public void setRtmpSslPort(int rtmpSslPort) { + this.rtmpSslPort = rtmpSslPort; + } + + public String getRtpAudioMtuSize() { + return rtpAudioMtuSize; + } + + public void setRtpAudioMtuSize(String rtpAudioMtuSize) { + this.rtpAudioMtuSize = rtpAudioMtuSize; + } + + public String getRtpClearCount() { + return rtpClearCount; + } + + public void setRtpClearCount(String rtpClearCount) { + this.rtpClearCount = rtpClearCount; + } + + public String getRtpCycleMS() { + return rtpCycleMS; + } + + public void setRtpCycleMS(String rtpCycleMS) { + this.rtpCycleMS = rtpCycleMS; + } + + public String getRtpMaxRtpCount() { + return rtpMaxRtpCount; + } + + public void setRtpMaxRtpCount(String rtpMaxRtpCount) { + this.rtpMaxRtpCount = rtpMaxRtpCount; + } + + public String getRtpVideoMtuSize() { + return rtpVideoMtuSize; + } + + public void setRtpVideoMtuSize(String rtpVideoMtuSize) { + this.rtpVideoMtuSize = rtpVideoMtuSize; + } + + public String getRtpProxyCheckSource() { + return rtpProxyCheckSource; + } + + public void setRtpProxyCheckSource(String rtpProxyCheckSource) { + this.rtpProxyCheckSource = rtpProxyCheckSource; + } + + public String getRtpProxyDumpDir() { + return rtpProxyDumpDir; + } + + public void setRtpProxyDumpDir(String rtpProxyDumpDir) { + this.rtpProxyDumpDir = rtpProxyDumpDir; + } + + public int getRtpProxyPort() { + return rtpProxyPort; + } + + public void setRtpProxyPort(int rtpProxyPort) { + this.rtpProxyPort = rtpProxyPort; + } + + public String getRtpProxyTimeoutSec() { + return rtpProxyTimeoutSec; + } + + public void setRtpProxyTimeoutSec(String rtpProxyTimeoutSec) { + this.rtpProxyTimeoutSec = rtpProxyTimeoutSec; + } + + public String getRtspAuthBasic() { + return rtspAuthBasic; + } + + public void setRtspAuthBasic(String rtspAuthBasic) { + this.rtspAuthBasic = rtspAuthBasic; + } + + public String getRtspHandshakeSecond() { + return rtspHandshakeSecond; + } + + public void setRtspHandshakeSecond(String rtspHandshakeSecond) { + this.rtspHandshakeSecond = rtspHandshakeSecond; + } + + public String getRtspKeepAliveSecond() { + return rtspKeepAliveSecond; + } + + public void setRtspKeepAliveSecond(String rtspKeepAliveSecond) { + this.rtspKeepAliveSecond = rtspKeepAliveSecond; + } + + public int getRtspPort() { + return rtspPort; + } + + public void setRtspPort(int rtspPort) { + this.rtspPort = rtspPort; + } + + public int getRtspSSlport() { + return rtspSSlport; + } + + public void setRtspSSlport(int rtspSSlport) { + this.rtspSSlport = rtspSSlport; + } + + public String getShellMaxReqSize() { + return shellMaxReqSize; + } + + public void setShellMaxReqSize(String shellMaxReqSize) { + this.shellMaxReqSize = shellMaxReqSize; + } + + public String getShellPhell() { + return shellPhell; + } + + public void setShellPhell(String shellPhell) { + this.shellPhell = shellPhell; + } + + public Float getHookAliveInterval() { + return hookAliveInterval; + } + + public void setHookAliveInterval(Float hookAliveInterval) { + this.hookAliveInterval = hookAliveInterval; + } + + public String getPortRange() { + return portRange; + } + + public void setPortRange(String portRange) { + this.portRange = portRange; + } + + public String getApiSnapRoot() { + return apiSnapRoot; + } + + public void setApiSnapRoot(String apiSnapRoot) { + this.apiSnapRoot = apiSnapRoot; + } + + public String getApiDefaultSnap() { + return apiDefaultSnap; + } + + public void setApiDefaultSnap(String apiDefaultSnap) { + this.apiDefaultSnap = apiDefaultSnap; + } + + public String getFfmpegSnap() { + return ffmpegSnap; + } + + public void setFfmpegSnap(String ffmpegSnap) { + this.ffmpegSnap = ffmpegSnap; + } + + public String getFfmpegRestartSec() { + return ffmpegRestartSec; + } + + public void setFfmpegRestartSec(String ffmpegRestartSec) { + this.ffmpegRestartSec = ffmpegRestartSec; + } + + public String getProtocolModifyStamp() { + return protocolModifyStamp; + } + + public void setProtocolModifyStamp(String protocolModifyStamp) { + this.protocolModifyStamp = protocolModifyStamp; + } + + public String getProtocolEnableAudio() { + return protocolEnableAudio; + } + + public void setProtocolEnableAudio(String protocolEnableAudio) { + this.protocolEnableAudio = protocolEnableAudio; + } + + public String getProtocolAddMuteAudio() { + return protocolAddMuteAudio; + } + + public void setProtocolAddMuteAudio(String protocolAddMuteAudio) { + this.protocolAddMuteAudio = protocolAddMuteAudio; + } + + public String getProtocolContinuePushMs() { + return protocolContinuePushMs; + } + + public void setProtocolContinuePushMs(String protocolContinuePushMs) { + this.protocolContinuePushMs = protocolContinuePushMs; + } + + public String getProtocolEnableHls() { + return protocolEnableHls; + } + + public void setProtocolEnableHls(String protocolEnableHls) { + this.protocolEnableHls = protocolEnableHls; + } + + public String getProtocolEnableMp4() { + return protocolEnableMp4; + } + + public void setProtocolEnableMp4(String protocolEnableMp4) { + this.protocolEnableMp4 = protocolEnableMp4; + } + + public String getProtocolEnableRtsp() { + return protocolEnableRtsp; + } + + public void setProtocolEnableRtsp(String protocolEnableRtsp) { + this.protocolEnableRtsp = protocolEnableRtsp; + } + + public String getProtocolEnableRtmp() { + return protocolEnableRtmp; + } + + public void setProtocolEnableRtmp(String protocolEnableRtmp) { + this.protocolEnableRtmp = protocolEnableRtmp; + } + + public String getProtocolEnableTs() { + return protocolEnableTs; + } + + public void setProtocolEnableTs(String protocolEnableTs) { + this.protocolEnableTs = protocolEnableTs; + } + + public String getProtocolEnableFmp4() { + return protocolEnableFmp4; + } + + public void setProtocolEnableFmp4(String protocolEnableFmp4) { + this.protocolEnableFmp4 = protocolEnableFmp4; + } + + public String getProtocolMp4AsPlayer() { + return protocolMp4AsPlayer; + } + + public void setProtocolMp4AsPlayer(String protocolMp4AsPlayer) { + this.protocolMp4AsPlayer = protocolMp4AsPlayer; + } + + public String getProtocolMp4MaxSecond() { + return protocolMp4MaxSecond; + } + + public void setProtocolMp4MaxSecond(String protocolMp4MaxSecond) { + this.protocolMp4MaxSecond = protocolMp4MaxSecond; + } + + public String getProtocolMp4SavePath() { + return protocolMp4SavePath; + } + + public void setProtocolMp4SavePath(String protocolMp4SavePath) { + this.protocolMp4SavePath = protocolMp4SavePath; + } + + public String getProtocolHlsSavePath() { + return protocolHlsSavePath; + } + + public void setProtocolHlsSavePath(String protocolHlsSavePath) { + this.protocolHlsSavePath = protocolHlsSavePath; + } + + public String getProtocolHlsDemand() { + return protocolHlsDemand; + } + + public void setProtocolHlsDemand(String protocolHlsDemand) { + this.protocolHlsDemand = protocolHlsDemand; + } + + public String getProtocolRtspDemand() { + return protocolRtspDemand; + } + + public void setProtocolRtspDemand(String protocolRtspDemand) { + this.protocolRtspDemand = protocolRtspDemand; + } + + public String getProtocolRtmpDemand() { + return protocolRtmpDemand; + } + + public void setProtocolRtmpDemand(String protocolRtmpDemand) { + this.protocolRtmpDemand = protocolRtmpDemand; + } + + public String getProtocolTsDemand() { + return protocolTsDemand; + } + + public void setProtocolTsDemand(String protocolTsDemand) { + this.protocolTsDemand = protocolTsDemand; + } + + public String getProtocolFmp4Demand() { + return protocolFmp4Demand; + } + + public void setProtocolFmp4Demand(String protocolFmp4Demand) { + this.protocolFmp4Demand = protocolFmp4Demand; + } + + public String getGeneralResetWhenRePlay() { + return generalResetWhenRePlay; + } + + public void setGeneralResetWhenRePlay(String generalResetWhenRePlay) { + this.generalResetWhenRePlay = generalResetWhenRePlay; + } + + public String getGeneralMergeWriteMS() { + return generalMergeWriteMS; + } + + public void setGeneralMergeWriteMS(String generalMergeWriteMS) { + this.generalMergeWriteMS = generalMergeWriteMS; + } + + public String getGeneralWaitTrackReadyMs() { + return generalWaitTrackReadyMs; + } + + public void setGeneralWaitTrackReadyMs(String generalWaitTrackReadyMs) { + this.generalWaitTrackReadyMs = generalWaitTrackReadyMs; + } + + public String getGeneralWaitAddTrackMs() { + return generalWaitAddTrackMs; + } + + public void setGeneralWaitAddTrackMs(String generalWaitAddTrackMs) { + this.generalWaitAddTrackMs = generalWaitAddTrackMs; + } + + public String getGeneralUnreadyFrameCache() { + return generalUnreadyFrameCache; + } + + public void setGeneralUnreadyFrameCache(String generalUnreadyFrameCache) { + this.generalUnreadyFrameCache = generalUnreadyFrameCache; + } + + public String getHlsSegRetain() { + return hlsSegRetain; + } + + public void setHlsSegRetain(String hlsSegRetain) { + this.hlsSegRetain = hlsSegRetain; + } + + public String getHlsBroadcastRecordTs() { + return hlsBroadcastRecordTs; + } + + public void setHlsBroadcastRecordTs(String hlsBroadcastRecordTs) { + this.hlsBroadcastRecordTs = hlsBroadcastRecordTs; + } + + public String getHlsDeleteDelaySec() { + return hlsDeleteDelaySec; + } + + public void setHlsDeleteDelaySec(String hlsDeleteDelaySec) { + this.hlsDeleteDelaySec = hlsDeleteDelaySec; + } + + public String getHlsSegKeep() { + return hlsSegKeep; + } + + public void setHlsSegKeep(String hlsSegKeep) { + this.hlsSegKeep = hlsSegKeep; + } + + public String getHookOnServerStarted() { + return hookOnServerStarted; + } + + public void setHookOnServerStarted(String hookOnServerStarted) { + this.hookOnServerStarted = hookOnServerStarted; + } + + public String getHookOnServerKeepalive() { + return hookOnServerKeepalive; + } + + public void setHookOnServerKeepalive(String hookOnServerKeepalive) { + this.hookOnServerKeepalive = hookOnServerKeepalive; + } + + public String getHookOnSendRtpStopped() { + return hookOnSendRtpStopped; + } + + public void setHookOnSendRtpStopped(String hookOnSendRtpStopped) { + this.hookOnSendRtpStopped = hookOnSendRtpStopped; + } + + public String getHookOnRtpServerTimeout() { + return hookOnRtpServerTimeout; + } + + public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) { + this.hookOnRtpServerTimeout = hookOnRtpServerTimeout; + } + + public String getTranscodeSuffix() { + return transcodeSuffix; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZlmHttpHookSubscribe.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZlmHttpHookSubscribe.java new file mode 100644 index 0000000..917ae71 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/ZlmHttpHookSubscribe.java @@ -0,0 +1,156 @@ +package com.yfd.monitor.media.zlm; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.media.zlm.dto.HookType; +import com.yfd.monitor.media.zlm.dto.IHookSubscribe; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * ZLMediaServer的hook事件订阅 + */ +@Component +public class ZlmHttpHookSubscribe { + + private final static Logger logger = LoggerFactory.getLogger(ZlmHttpHookSubscribe.class); + + @FunctionalInterface + public interface Event{ + void response(MediaServerItem mediaServerItem, JSONObject response); + } + + private Map> allSubscribes = new ConcurrentHashMap<>(); + + public void addSubscribe(IHookSubscribe hookSubscribe, ZlmHttpHookSubscribe.Event event) { + if (hookSubscribe.getExpires() == null) { + // 默认5分钟过期 + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5)); + hookSubscribe.setExpires(expiresInstant); + } + allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event); + } + + public ZlmHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) { + ZlmHttpHookSubscribe.Event event= null; + Map eventMap = allSubscribes.get(type); + if (eventMap == null) { + return null; + } + for (IHookSubscribe key : eventMap.keySet()) { + Boolean result = null; + for (String s : key.getContent().keySet()) { + if (result == null) { + result = key.getContent().getString(s).equals(hookResponse.getString(s)); + }else { + if (key.getContent().getString(s) == null) { + continue; + } + result = result && key.getContent().getString(s).equals(hookResponse.getString(s)); + } + } + if (null != result && result) { + event = eventMap.get(key); + } + } + return event; + } + + + + public void removeSubscribe(IHookSubscribe hookSubscribe) { + Map eventMap = allSubscribes.get(hookSubscribe.getHookType()); + if (eventMap == null) { + return; + } + + Set> entries = eventMap.entrySet(); + if (entries.size() > 0) { + List> entriesToRemove = new ArrayList<>(); + for (Map.Entry entry : entries) { + JSONObject content = entry.getKey().getContent(); + if (content == null || content.size() == 0) { + entriesToRemove.add(entry); + continue; + } + Boolean result = null; + for (String s : content.keySet()) { + if (result == null) { + result = content.getString(s).equals(hookSubscribe.getContent().getString(s)); + }else { + if (content.getString(s) == null) { + continue; + } + result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s)); + } + } + if (result){ + entriesToRemove.add(entry); + } + } + + if (!CollectionUtils.isEmpty(entriesToRemove)) { + for (Map.Entry entry : entriesToRemove) { + entries.remove(entry); + } + } + + } + } + + /** + * 获取某个类型的所有的订阅 + * @param type + * @return + */ + public List getSubscribes(HookType type) { + Map eventMap = allSubscribes.get(type); + if (eventMap == null) { + return null; + } + List result = new ArrayList<>(); + for (IHookSubscribe key : eventMap.keySet()) { + result.add(eventMap.get(key)); + } + return result; + } + + public List getAll(){ + ArrayList result = new ArrayList<>(); + Collection> values = allSubscribes.values(); + for (Map value : values) { + result.addAll(value.keySet()); + } + return result; + } + + /** + * 对订阅数据进行过期清理 + */ + @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 + public void execute(){ + + Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); + int total = 0; + for (HookType hookType : allSubscribes.keySet()) { + Map hookSubscribeEventMap = allSubscribes.get(hookType); + if (hookSubscribeEventMap.size() > 0) { + for (IHookSubscribe hookSubscribe : hookSubscribeEventMap.keySet()) { + if (hookSubscribe.getExpires().isBefore(instant)) { + // 过期的 + hookSubscribeEventMap.remove(hookSubscribe); + total ++; + } + } + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ChannelOnlineEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ChannelOnlineEvent.java new file mode 100644 index 0000000..10cc527 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ChannelOnlineEvent.java @@ -0,0 +1,9 @@ +package com.yfd.monitor.media.zlm.dto; + +import java.text.ParseException; + + +public interface ChannelOnlineEvent { + + void run(String app, String stream, String serverId) throws ParseException; +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeFactory.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeFactory.java new file mode 100644 index 0000000..bedfa19 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeFactory.java @@ -0,0 +1,55 @@ +package com.yfd.monitor.media.zlm.dto; + + +import com.alibaba.fastjson2.JSONObject; + +/** + * hook 订阅工厂 +* + */ +public class HookSubscribeFactory { + + public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) { + HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange(); + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject(); + subscribeKey.put("app", app); + subscribeKey.put("stream", stream); + subscribeKey.put("regist", regist); + if (scheam != null) { + subscribeKey.put("schema", scheam); + } + subscribeKey.put("mediaServerId", mediaServerId); + hookSubscribe.setContent(subscribeKey); + + return hookSubscribe; + } + + public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) { + HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout(); + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject(); + subscribeKey.put("stream_id", stream); + subscribeKey.put("ssrc", ssrc); + subscribeKey.put("mediaServerId", mediaServerId); + hookSubscribe.setContent(subscribeKey); + + return hookSubscribe; + } + + public static HookSubscribeForServerStarted on_server_started() { + HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); + hookSubscribe.setContent(new JSONObject()); + + return hookSubscribe; + } + + public static HookSubscribeForRecordMp4 on_record_mp4(String mediaServerId, String app, String stream) { + HookSubscribeForRecordMp4 hookSubscribe = new HookSubscribeForRecordMp4(); + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject(); + subscribeKey.put("app", app); + subscribeKey.put("stream", stream); + subscribeKey.put("mediaServerId", mediaServerId); + hookSubscribe.setContent(subscribeKey); + + return hookSubscribe; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForRecordMp4.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForRecordMp4.java new file mode 100644 index 0000000..b91f31e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForRecordMp4.java @@ -0,0 +1,44 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-录像完成 + * @author lin + */ +public class HookSubscribeForRecordMp4 implements IHookSubscribe{ + + private HookType hookType = HookType.on_record_mp4; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForRtpServerTimeout.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForRtpServerTimeout.java new file mode 100644 index 0000000..bff61fd --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForRtpServerTimeout.java @@ -0,0 +1,44 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-收流超时 +* + */ +public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{ + + private HookType hookType = HookType.on_rtp_server_timeout; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForServerStarted.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForServerStarted.java new file mode 100644 index 0000000..4617d54 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForServerStarted.java @@ -0,0 +1,44 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-流变化 +* + */ +public class HookSubscribeForServerStarted implements IHookSubscribe{ + + private HookType hookType = HookType.on_server_started; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForStreamChange.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForStreamChange.java new file mode 100644 index 0000000..4b0715f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookSubscribeForStreamChange.java @@ -0,0 +1,44 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-流变化 +* + */ +public class HookSubscribeForStreamChange implements IHookSubscribe{ + + private HookType hookType = HookType.on_stream_changed; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookType.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookType.java new file mode 100644 index 0000000..634bb2c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/HookType.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.media.zlm.dto; + +/** + * hook类型 +* + */ + +public enum HookType { + + on_flow_report, + on_http_access, + on_play, + on_publish, + on_record_mp4, + on_rtsp_auth, + on_rtsp_realm, + on_shell_login, + on_stream_changed, + on_stream_none_reader, + on_stream_not_found, + on_server_started, + + on_rtp_server_timeout, + on_server_keepalive +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/IHookSubscribe.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/IHookSubscribe.java new file mode 100644 index 0000000..36f072f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/IHookSubscribe.java @@ -0,0 +1,36 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; + +import java.time.Instant; + +/** + * zlm hook事件的参数 +* + */ +public interface IHookSubscribe { + + /** + * 获取hook类型 + * @return hook类型 + */ + HookType getHookType(); + + /** + * 获取hook的具体内容 + * @return hook的具体内容 + */ + JSONObject getContent(); + + /** + * 设置过期时间 + * @param instant 过期时间 + */ + void setExpires(Instant instant); + + /** + * 获取过期时间 + * @return 过期时间 + */ + Instant getExpires(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/MediaServerItem.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/MediaServerItem.java new file mode 100644 index 0000000..90e82b8 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/MediaServerItem.java @@ -0,0 +1,310 @@ +package com.yfd.monitor.media.zlm.dto; + + +import com.yfd.monitor.media.zlm.ZLMServerConfig; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.ObjectUtils; + +@Schema(description = "流媒体服务信息") +public class MediaServerItem{ + + @Schema(description = "ID") + private String id; + + @Schema(description = "IP") + private String ip; + + @Schema(description = "hook使用的IP(zlm访问WVP使用的IP)") + private String hookIp; + + @Schema(description = "SDP IP") + private String sdpIp; + + @Schema(description = "流IP") + private String streamIp; + + @Schema(description = "HTTP端口") + private int httpPort; + + @Schema(description = "HTTPS端口") + private int httpSSlPort; + + @Schema(description = "RTMP端口") + private int rtmpPort; + + @Schema(description = "RTMPS端口") + private int rtmpSSlPort; + + @Schema(description = "RTP收流端口(单端口模式有用)") + private int rtpProxyPort; + + @Schema(description = "RTSP端口") + private int rtspPort; + + @Schema(description = "RTSPS端口") + private int rtspSSLPort; + + @Schema(description = "是否开启自动配置ZLM") + private boolean autoConfig; + + @Schema(description = "ZLM鉴权参数") + private String secret; + + @Schema(description = "keepalive hook触发间隔,单位秒") + private Float hookAliveInterval; + + @Schema(description = "是否使用多端口模式") + private boolean rtpEnable; + + @Schema(description = "状态") + private boolean status; + + @Schema(description = "多端口RTP收流端口范围") + private String rtpPortRange; + + @Schema(description = "assist服务端口") + private int recordAssistPort; + + @Schema(description = "创建时间") + private String createTime; + + @Schema(description = "更新时间") + private String updateTime; + + @Schema(description = "上次心跳时间") + private String lastKeepaliveTime; + + @Schema(description = "是否是默认ZLM") + private boolean defaultServer; + + @Schema(description = "当前使用到的端口") + private int currentPort; + + @Schema(description = "类型: zlm/abl") + private String type; + + public MediaServerItem() { + } + + public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) { + id = zlmServerConfig.getGeneralMediaServerId(); + ip = zlmServerConfig.getIp(); + hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp(); + sdpIp = ObjectUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp(); + streamIp = ObjectUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp(); + httpPort = zlmServerConfig.getHttpPort(); + httpSSlPort = zlmServerConfig.getHttpSSLport(); + rtmpPort = zlmServerConfig.getRtmpPort(); + rtmpSSlPort = zlmServerConfig.getRtmpSslPort(); + rtpProxyPort = zlmServerConfig.getRtpProxyPort(); + rtspPort = zlmServerConfig.getRtspPort(); + rtspSSLPort = zlmServerConfig.getRtspSSlport(); + autoConfig = true; // 默认值true; + secret = zlmServerConfig.getApiSecret(); + hookAliveInterval = zlmServerConfig.getHookAliveInterval(); + rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 + rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 + recordAssistPort = 0; // 默认关闭 + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getHookIp() { + return hookIp; + } + + public void setHookIp(String hookIp) { + this.hookIp = hookIp; + } + + public String getSdpIp() { + return sdpIp; + } + + public void setSdpIp(String sdpIp) { + this.sdpIp = sdpIp; + } + + public String getStreamIp() { + return streamIp; + } + + public void setStreamIp(String streamIp) { + this.streamIp = streamIp; + } + + public int getHttpPort() { + return httpPort; + } + + public void setHttpPort(int httpPort) { + this.httpPort = httpPort; + } + + public int getHttpSSlPort() { + return httpSSlPort; + } + + public void setHttpSSlPort(int httpSSlPort) { + this.httpSSlPort = httpSSlPort; + } + + public int getRtmpPort() { + return rtmpPort; + } + + public void setRtmpPort(int rtmpPort) { + this.rtmpPort = rtmpPort; + } + + public int getRtmpSSlPort() { + return rtmpSSlPort; + } + + public void setRtmpSSlPort(int rtmpSSlPort) { + this.rtmpSSlPort = rtmpSSlPort; + } + + public int getRtpProxyPort() { + return rtpProxyPort; + } + + public void setRtpProxyPort(int rtpProxyPort) { + this.rtpProxyPort = rtpProxyPort; + } + + public int getRtspPort() { + return rtspPort; + } + + public void setRtspPort(int rtspPort) { + this.rtspPort = rtspPort; + } + + public int getRtspSSLPort() { + return rtspSSLPort; + } + + public void setRtspSSLPort(int rtspSSLPort) { + this.rtspSSLPort = rtspSSLPort; + } + + public boolean isAutoConfig() { + return autoConfig; + } + + public void setAutoConfig(boolean autoConfig) { + this.autoConfig = autoConfig; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public boolean isRtpEnable() { + return rtpEnable; + } + + public void setRtpEnable(boolean rtpEnable) { + this.rtpEnable = rtpEnable; + } + + public String getRtpPortRange() { + return rtpPortRange; + } + + public void setRtpPortRange(String rtpPortRange) { + this.rtpPortRange = rtpPortRange; + } + + public int getRecordAssistPort() { + return recordAssistPort; + } + + public void setRecordAssistPort(int recordAssistPort) { + this.recordAssistPort = recordAssistPort; + } + + public boolean isDefaultServer() { + return defaultServer; + } + + public void setDefaultServer(boolean defaultServer) { + this.defaultServer = defaultServer; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getCurrentPort() { + return currentPort; + } + + public void setCurrentPort(int currentPort) { + this.currentPort = currentPort; + } + + public boolean isStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public String getLastKeepaliveTime() { + return lastKeepaliveTime; + } + + public void setLastKeepaliveTime(String lastKeepaliveTime) { + this.lastKeepaliveTime = lastKeepaliveTime; + } + + public Float getHookAliveInterval() { + return hookAliveInterval; + } + + public void setHookAliveInterval(Float hookAliveInterval) { + this.hookAliveInterval = hookAliveInterval; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ServerKeepaliveData.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ServerKeepaliveData.java new file mode 100644 index 0000000..b565881 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ServerKeepaliveData.java @@ -0,0 +1,4 @@ +package com.yfd.monitor.media.zlm.dto; + +public class ServerKeepaliveData { +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamAuthorityInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamAuthorityInfo.java new file mode 100644 index 0000000..6f25115 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamAuthorityInfo.java @@ -0,0 +1,117 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.yfd.monitor.media.zlm.dto.hook.OnPublishHookParam; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; + +/** + * 流的鉴权信息 +* + */ +public class StreamAuthorityInfo { + + private String id; + private String app; + private String stream; + + /** + * 产生源类型, + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7 + */ + private int originType; + + /** + * 产生源类型的字符串描述 + */ + private String originTypeStr; + + /** + * 推流时自定义的播放鉴权ID + */ + private String callId; + + /** + * 推流的鉴权签名 + */ + private String sign; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public int getOriginType() { + return originType; + } + + public void setOriginType(int originType) { + this.originType = originType; + } + + public String getOriginTypeStr() { + return originTypeStr; + } + + public void setOriginTypeStr(String originTypeStr) { + this.originTypeStr = originTypeStr; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public static StreamAuthorityInfo getInstanceByHook(OnPublishHookParam hookParam) { + StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo(); + streamAuthorityInfo.setApp(hookParam.getApp()); + streamAuthorityInfo.setStream(hookParam.getStream()); + streamAuthorityInfo.setId(hookParam.getId()); + return streamAuthorityInfo; + } + + public static StreamAuthorityInfo getInstanceByHook(OnStreamChangedHookParam onStreamChangedHookParam) { + StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo(); + streamAuthorityInfo.setApp(onStreamChangedHookParam.getApp()); + streamAuthorityInfo.setStream(onStreamChangedHookParam.getStream()); + streamAuthorityInfo.setId(onStreamChangedHookParam.getMediaServerId()); + streamAuthorityInfo.setOriginType(onStreamChangedHookParam.getOriginType()); + streamAuthorityInfo.setOriginTypeStr(onStreamChangedHookParam.getOriginTypeStr()); + return streamAuthorityInfo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamProxyItem.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamProxyItem.java new file mode 100644 index 0000000..aaaf7a1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamProxyItem.java @@ -0,0 +1,179 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import io.swagger.v3.oas.annotations.media.Schema; + + +@Schema(description = "拉流代理的信息") +public class StreamProxyItem extends GbStream { + + @Schema(description = "类型") + private String type; + @Schema(description = "应用名") + private String app; + @Schema(description = "流ID") + private String stream; + @Schema(description = "流媒体服务ID") + private String mediaServerId; + @Schema(description = "拉流地址") + private String url; + @Schema(description = "拉流地址") + private String src_url; + @Schema(description = "目标地址") + private String dst_url; + @Schema(description = "超时时间") + private int timeout_ms; + @Schema(description = "ffmpeg模板KEY") + private String ffmpeg_cmd_key; + @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") + private String rtp_type; + @Schema(description = "是否启用") + private boolean enable; + @Schema(description = "是否启用音频") + private boolean enable_audio; + @Schema(description = "是否启用MP4") + private boolean enable_mp4; + @Schema(description = "是否 无人观看时删除") + private boolean enable_remove_none_reader; + + @Schema(description = "是否 无人观看时自动停用") + private boolean enable_disable_none_reader; + @Schema(description = "创建时间") + private String createTime; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String getApp() { + return app; + } + + @Override + public void setApp(String app) { + this.app = app; + } + + @Override + public String getStream() { + return stream; + } + + @Override + public void setStream(String stream) { + this.stream = stream; + } + + @Override + public String getMediaServerId() { + return mediaServerId; + } + + @Override + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getSrc_url() { + return src_url; + } + + public void setSrc_url(String src_url) { + this.src_url = src_url; + } + + public String getDst_url() { + return dst_url; + } + + public void setDst_url(String dst_url) { + this.dst_url = dst_url; + } + + public int getTimeout_ms() { + return timeout_ms; + } + + public void setTimeout_ms(int timeout_ms) { + this.timeout_ms = timeout_ms; + } + + public String getFfmpeg_cmd_key() { + return ffmpeg_cmd_key; + } + + public void setFfmpeg_cmd_key(String ffmpeg_cmd_key) { + this.ffmpeg_cmd_key = ffmpeg_cmd_key; + } + + public String getRtp_type() { + return rtp_type; + } + + public void setRtp_type(String rtp_type) { + this.rtp_type = rtp_type; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public boolean isEnable_mp4() { + return enable_mp4; + } + + public void setEnable_mp4(boolean enable_mp4) { + this.enable_mp4 = enable_mp4; + } + + @Override + public String getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public boolean isEnable_remove_none_reader() { + return enable_remove_none_reader; + } + + public void setEnable_remove_none_reader(boolean enable_remove_none_reader) { + this.enable_remove_none_reader = enable_remove_none_reader; + } + + public boolean isEnable_disable_none_reader() { + return enable_disable_none_reader; + } + + public void setEnable_disable_none_reader(boolean enable_disable_none_reader) { + this.enable_disable_none_reader = enable_disable_none_reader; + } + + public boolean isEnable_audio() { + return enable_audio; + } + + public void setEnable_audio(boolean enable_audio) { + this.enable_audio = enable_audio; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamPushItem.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamPushItem.java new file mode 100644 index 0000000..e820b11 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/StreamPushItem.java @@ -0,0 +1,329 @@ +package com.yfd.monitor.media.zlm.dto; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +@Schema(description = "推流信息") +public class StreamPushItem extends GbStream implements Comparable{ + + /** + * id + */ + @Schema(description = "id") + private Integer id; + + /** + * 应用名 + */ + @Schema(description = "应用名") + private String app; + + /** + * 流id + */ + @Schema(description = "流id") + private String stream; + + /** + * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv + */ + @Schema(description = "观看总人数") + private String totalReaderCount; + + /** + * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv + */ + @Schema(description = "协议 包括hls/rtsp/rtmp/http-flv/ws-flv") + private List schemas; + + /** + * 产生源类型, + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7 + */ + @Schema(description = "产生源类型") + private int originType; + + /** + * 客户端和服务器网络信息,可能为null类型 + */ + @Schema(description = "客户端和服务器网络信息,可能为null类型") + private OnStreamChangedHookParam.OriginSock originSock; + + /** + * 产生源类型的字符串描述 + */ + @Schema(description = "产生源类型的字符串描述") + private String originTypeStr; + + /** + * 产生源的url + */ + @Schema(description = "产生源的url") + private String originUrl; + + /** + * 存活时间,单位秒 + */ + @Schema(description = "存活时间,单位秒") + private Long aliveSecond; + + /** + * 音视频轨道 + */ + @Schema(description = "音视频轨道") + private List tracks; + + /** + * 音视频轨道 + */ + @Schema(description = "音视频轨道") + private String vhost; + + /** + * 使用的流媒体ID + */ + @Schema(description = "使用的流媒体ID") + private String mediaServerId; + + /** + * 使用的服务ID + */ + @Schema(description = "使用的服务ID") + private String serverId; + + /** + * 推流时间 + */ + @Schema(description = "推流时间") + private String pushTime; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + private String updateTime; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + private String createTime; + + /** + * 是否正在推流 + */ + @Schema(description = "是否正在推流") + private boolean pushIng; + + /** + * 是否自己平台的推流 + */ + @Schema(description = "是否自己平台的推流") + private boolean self; + + + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + + @Override + public int compareTo(@NotNull StreamPushItem streamPushItem) { + return Long.valueOf(DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(super.createTime) + - DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(streamPushItem.getCreateTime())).intValue(); + } + + public static class MediaSchema { + private String schema; + private Long bytesSpeed; + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public Long getBytesSpeed() { + return bytesSpeed; + } + + public void setBytesSpeed(Long bytesSpeed) { + this.bytesSpeed = bytesSpeed; + } + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + @Override + public String getApp() { + return app; + } + + @Override + public void setApp(String app) { + this.app = app; + } + + @Override + public String getStream() { + return stream; + } + + @Override + public void setStream(String stream) { + this.stream = stream; + } + + public String getTotalReaderCount() { + return totalReaderCount; + } + + public void setTotalReaderCount(String totalReaderCount) { + this.totalReaderCount = totalReaderCount; + } + + public List getSchemas() { + return schemas; + } + + public void setSchemas(List schemas) { + this.schemas = schemas; + } + + public int getOriginType() { + return originType; + } + + public void setOriginType(int originType) { + this.originType = originType; + } + + public OnStreamChangedHookParam.OriginSock getOriginSock() { + return originSock; + } + + public void setOriginSock(OnStreamChangedHookParam.OriginSock originSock) { + this.originSock = originSock; + } + + + public String getOriginTypeStr() { + return originTypeStr; + } + + public void setOriginTypeStr(String originTypeStr) { + this.originTypeStr = originTypeStr; + } + + public String getOriginUrl() { + return originUrl; + } + + public void setOriginUrl(String originUrl) { + this.originUrl = originUrl; + } + + public Long getAliveSecond() { + return aliveSecond; + } + + public void setAliveSecond(Long aliveSecond) { + this.aliveSecond = aliveSecond; + } + + public List getTracks() { + return tracks; + } + + public void setTracks(List tracks) { + this.tracks = tracks; + } + + + @Override + public String getMediaServerId() { + return mediaServerId; + } + + @Override + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + + public String getPushTime() { + return pushTime; + } + + public void setPushTime(String pushTime) { + this.pushTime = pushTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public boolean isPushIng() { + return pushIng; + } + + public void setPushIng(boolean pushIng) { + this.pushIng = pushIng; + } + + public boolean isSelf() { + return self; + } + + public void setSelf(boolean self) { + this.self = self; + } +} + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ZLMRunInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ZLMRunInfo.java new file mode 100644 index 0000000..975f50a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/ZLMRunInfo.java @@ -0,0 +1,33 @@ +package com.yfd.monitor.media.zlm.dto; + +/** + * 记录zlm运行中一些参数 + */ +public class ZLMRunInfo { + + /** + * zlm当前流数量 + */ + private int mediaCount; + + /** + * 在线状态 + */ + private boolean online; + + public int getMediaCount() { + return mediaCount; + } + + public void setMediaCount(int mediaCount) { + this.mediaCount = mediaCount; + } + + public boolean isOnline() { + return online; + } + + public void setOnline(boolean online) { + this.online = online; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookParam.java new file mode 100644 index 0000000..0ca5f8d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookParam.java @@ -0,0 +1,16 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件的参数 + */ +public class HookParam { + private String mediaServerId; + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookResult.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookResult.java new file mode 100644 index 0000000..44184be --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookResult.java @@ -0,0 +1,36 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +public class HookResult { + + private int code; + private String msg; + + + public HookResult() { + } + + public HookResult(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public static HookResult SUCCESS(){ + return new HookResult(0, "success"); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookResultForOnPublish.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookResultForOnPublish.java new file mode 100644 index 0000000..503d101 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/HookResultForOnPublish.java @@ -0,0 +1,53 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +public class HookResultForOnPublish extends HookResult{ + + private boolean enable_audio; + private boolean enable_mp4; + private int mp4_max_second; + private String mp4_save_path; + + public HookResultForOnPublish() { + } + + public static HookResultForOnPublish SUCCESS(){ + return new HookResultForOnPublish(0, "success"); + } + + public HookResultForOnPublish(int code, String msg) { + setCode(code); + setMsg(msg); + } + + public boolean isEnable_audio() { + return enable_audio; + } + + public void setEnable_audio(boolean enable_audio) { + this.enable_audio = enable_audio; + } + + public boolean isEnable_mp4() { + return enable_mp4; + } + + public void setEnable_mp4(boolean enable_mp4) { + this.enable_mp4 = enable_mp4; + } + + public int getMp4_max_second() { + return mp4_max_second; + } + + public void setMp4_max_second(int mp4_max_second) { + this.mp4_max_second = mp4_max_second; + } + + public String getMp4_save_path() { + return mp4_save_path; + } + + public void setMp4_save_path(String mp4_save_path) { + this.mp4_save_path = mp4_save_path; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnPlayHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnPlayHookParam.java new file mode 100644 index 0000000..e1ad3e9 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnPlayHookParam.java @@ -0,0 +1,86 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_play事件的参数 +* + */ +public class OnPlayHookParam extends HookParam{ + private String id; + private String app; + private String stream; + private String ip; + private String params; + private int port; + private String schema; + private String vhost; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnPublishHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnPublishHookParam.java new file mode 100644 index 0000000..7a465da --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnPublishHookParam.java @@ -0,0 +1,85 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_publish事件的参数 + */ +public class OnPublishHookParam extends HookParam{ + private String id; + private String app; + private String stream; + private String ip; + private String params; + private int port; + private String schema; + private String vhost; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnRecordMp4HookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnRecordMp4HookParam.java new file mode 100644 index 0000000..6e89f24 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnRecordMp4HookParam.java @@ -0,0 +1,114 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_rtp_server_timeout事件的参数 + * @author lin + */ +public class OnRecordMp4HookParam extends HookParam{ + private String app; + private String stream; + private String file_name; + private String file_path; + private long file_size; + private String folder; + private String url; + private String vhost; + private long start_time; + private double time_len; + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getFile_name() { + return file_name; + } + + public void setFile_name(String file_name) { + this.file_name = file_name; + } + + public String getFile_path() { + return file_path; + } + + public void setFile_path(String file_path) { + this.file_path = file_path; + } + + public long getFile_size() { + return file_size; + } + + public void setFile_size(long file_size) { + this.file_size = file_size; + } + + public String getFolder() { + return folder; + } + + public void setFolder(String folder) { + this.folder = folder; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + public long getStart_time() { + return start_time; + } + + public void setStart_time(long start_time) { + this.start_time = start_time; + } + + public double getTime_len() { + return time_len; + } + + public void setTime_len(double time_len) { + this.time_len = time_len; + } + + @Override + public String toString() { + return "OnRecordMp4HookParam{" + + "app='" + app + '\'' + + ", stream='" + stream + '\'' + + ", file_name='" + file_name + '\'' + + ", file_path='" + file_path + '\'' + + ", file_size='" + file_size + '\'' + + ", folder='" + folder + '\'' + + ", url='" + url + '\'' + + ", vhost='" + vhost + '\'' + + ", start_time=" + start_time + + ", time_len=" + time_len + + '}'; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java new file mode 100644 index 0000000..ab9c201 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java @@ -0,0 +1,52 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_rtp_server_timeout事件的参数 + */ +public class OnRtpServerTimeoutHookParam extends HookParam{ + private int local_port; + private String stream_id; + private int tcpMode; + private boolean re_use_port; + private String ssrc; + + public int getLocal_port() { + return local_port; + } + + public void setLocal_port(int local_port) { + this.local_port = local_port; + } + + public String getStream_id() { + return stream_id; + } + + public void setStream_id(String stream_id) { + this.stream_id = stream_id; + } + + public int getTcpMode() { + return tcpMode; + } + + public void setTcpMode(int tcpMode) { + this.tcpMode = tcpMode; + } + + public boolean isRe_use_port() { + return re_use_port; + } + + public void setRe_use_port(boolean re_use_port) { + this.re_use_port = re_use_port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java new file mode 100644 index 0000000..6c5981f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java @@ -0,0 +1,26 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_send_rtp_stopped事件的参数 + */ +public class OnSendRtpStoppedHookParam extends HookParam{ + private String app; + private String stream; + + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnServerKeepaliveHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnServerKeepaliveHookParam.java new file mode 100644 index 0000000..f4a9d1a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnServerKeepaliveHookParam.java @@ -0,0 +1,20 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +import com.yfd.monitor.media.zlm.dto.ServerKeepaliveData; + +/** + * zlm hook事件中的on_play事件的参数 +* + */ +public class OnServerKeepaliveHookParam extends HookParam{ + + private ServerKeepaliveData data; + + public ServerKeepaliveData getData() { + return data; + } + + public void setData(ServerKeepaliveData data) { + this.data = data; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamChangedHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamChangedHookParam.java new file mode 100644 index 0000000..c7d68b9 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamChangedHookParam.java @@ -0,0 +1,431 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +import com.yfd.monitor.vmanager.bean.StreamContent; + +import java.util.List; + + +public class OnStreamChangedHookParam extends HookParam{ + + /** + * 注册/注销 + */ + private boolean regist; + + /** + * 应用名 + */ + private String app; + + /** + * 流id + */ + private String stream; + + /** + * 推流鉴权Id + */ + private String callId; + + /** + * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv + */ + private String totalReaderCount; + + /** + * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv + */ + private String schema; + + + /** + * 产生源类型, + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7 + */ + private int originType; + + /** + * 客户端和服务器网络信息,可能为null类型 + */ + private OriginSock originSock; + + /** + * 产生源类型的字符串描述 + */ + private String originTypeStr; + + /** + * 产生源的url + */ + private String originUrl; + + /** + * 服务器id + */ + private String severId; + + /** + * GMT unix系统时间戳,单位秒 + */ + private Long createStamp; + + /** + * 存活时间,单位秒 + */ + private Long aliveSecond; + + /** + * 数据产生速度,单位byte/s + */ + private Long bytesSpeed; + + /** + * 音视频轨道 + */ + private List tracks; + + /** + * 音视频轨道 + */ + private String vhost; + + public boolean isRegist() { + return regist; + } + + public void setRegist(boolean regist) { + this.regist = regist; + } + + /** + * 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改 + */ + private boolean docker; + + public static class MediaTrack { + /** + * 音频通道数 + */ + private int channels; + + /** + * H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4 + */ + private int codecId; + + /** + * 编码类型名称 CodecAAC CodecH264 + */ + private String codecIdName; + + /** + * Video = 0, Audio = 1 + */ + private int codecType; + + /** + * 轨道是否准备就绪 + */ + private boolean ready; + + /** + * 音频采样位数 + */ + private int sampleBit; + + /** + * 音频采样率 + */ + private int sampleRate; + + /** + * 视频fps + */ + private int fps; + + /** + * 视频高 + */ + private int height; + + /** + * 视频宽 + */ + private int width; + + public int getChannels() { + return channels; + } + + public void setChannels(int channels) { + this.channels = channels; + } + + public int getCodecId() { + return codecId; + } + + public void setCodecId(int codecId) { + this.codecId = codecId; + } + + public String getCodecIdName() { + return codecIdName; + } + + public void setCodecIdName(String codecIdName) { + this.codecIdName = codecIdName; + } + + public int getCodecType() { + return codecType; + } + + public void setCodecType(int codecType) { + this.codecType = codecType; + } + + public boolean isReady() { + return ready; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + + public int getSampleBit() { + return sampleBit; + } + + public void setSampleBit(int sampleBit) { + this.sampleBit = sampleBit; + } + + public int getSampleRate() { + return sampleRate; + } + + public void setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + } + + public int getFps() { + return fps; + } + + public void setFps(int fps) { + this.fps = fps; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + } + + public static class OriginSock{ + private String identifier; + private String local_ip; + private int local_port; + private String peer_ip; + private int peer_port; + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getLocal_ip() { + return local_ip; + } + + public void setLocal_ip(String local_ip) { + this.local_ip = local_ip; + } + + public int getLocal_port() { + return local_port; + } + + public void setLocal_port(int local_port) { + this.local_port = local_port; + } + + public String getPeer_ip() { + return peer_ip; + } + + public void setPeer_ip(String peer_ip) { + this.peer_ip = peer_ip; + } + + public int getPeer_port() { + return peer_port; + } + + public void setPeer_port(int peer_port) { + this.peer_port = peer_port; + } + } + + private StreamContent streamInfo; + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getTotalReaderCount() { + return totalReaderCount; + } + + public void setTotalReaderCount(String totalReaderCount) { + this.totalReaderCount = totalReaderCount; + } + + + public int getOriginType() { + return originType; + } + + public void setOriginType(int originType) { + this.originType = originType; + } + + + public String getOriginTypeStr() { + return originTypeStr; + } + + public void setOriginTypeStr(String originTypeStr) { + this.originTypeStr = originTypeStr; + } + + public String getOriginUrl() { + return originUrl; + } + + public void setOriginUrl(String originUrl) { + this.originUrl = originUrl; + } + + public Long getCreateStamp() { + return createStamp; + } + + public void setCreateStamp(Long createStamp) { + this.createStamp = createStamp; + } + + public Long getAliveSecond() { + return aliveSecond; + } + + public void setAliveSecond(Long aliveSecond) { + this.aliveSecond = aliveSecond; + } + + public List getTracks() { + return tracks; + } + + public void setTracks(List tracks) { + this.tracks = tracks; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public void setOriginSock(OriginSock originSock) { + this.originSock = originSock; + } + + public Long getBytesSpeed() { + return bytesSpeed; + } + + public void setBytesSpeed(Long bytesSpeed) { + this.bytesSpeed = bytesSpeed; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + public OriginSock getOriginSock() { + return originSock; + } + + public boolean isDocker() { + return docker; + } + + public void setDocker(boolean docker) { + this.docker = docker; + } + + public StreamContent getStreamInfo() { + return streamInfo; + } + + public void setStreamInfo(StreamContent streamInfo) { + this.streamInfo = streamInfo; + } + + public String getSeverId() { + return severId; + } + + public void setSeverId(String severId) { + this.severId = severId; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamNoneReaderHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamNoneReaderHookParam.java new file mode 100644 index 0000000..51193c3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamNoneReaderHookParam.java @@ -0,0 +1,41 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +public class OnStreamNoneReaderHookParam extends HookParam{ + + private String schema; + private String app; + private String stream; + private String vhost; + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamNotFoundHookParam.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamNotFoundHookParam.java new file mode 100644 index 0000000..059630c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OnStreamNotFoundHookParam.java @@ -0,0 +1,85 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +/** + * zlm hook事件中的on_stream_not_found事件的参数 + */ +public class OnStreamNotFoundHookParam extends HookParam{ + private String id; + private String app; + private String stream; + private String ip; + private String params; + private int port; + private String schema; + private String vhost; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getVhost() { + return vhost; + } + + public void setVhost(String vhost) { + this.vhost = vhost; + } + + @Override + public String toString() { + return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OriginType.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OriginType.java new file mode 100644 index 0000000..abb8326 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/dto/hook/OriginType.java @@ -0,0 +1,23 @@ +package com.yfd.monitor.media.zlm.dto.hook; + +public enum OriginType { + // 不可调整顺序 + UNKNOWN("UNKNOWN"), + RTMP_PUSH("PUSH"), + RTSP_PUSH("PUSH"), + RTP_PUSH("RTP"), + PULL("PULL"), + FFMPEG_PULL("PULL"), + MP4_VOD("MP4_VOD"), + DEVICE_CHN("DEVICE_CHN"), + RTC_PUSH("PUSH"); + + private final String type; + OriginType(String type) { + this.type = type; + } + + public String getType() { + return type; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMEventAbstract.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMEventAbstract.java new file mode 100644 index 0000000..2a306eb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMEventAbstract.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.media.zlm.event; + +import com.yfd.monitor.media.zlm.ZLMServerConfig; +import org.springframework.context.ApplicationEvent; + +public abstract class ZLMEventAbstract extends ApplicationEvent { + + + private static final long serialVersionUID = 1L; + + private String mediaServerId; + + + public ZLMEventAbstract(Object source) { + super(source); + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMMediaNodeServerService.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMMediaNodeServerService.java new file mode 100644 index 0000000..8c6e412 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMMediaNodeServerService.java @@ -0,0 +1,389 @@ +package com.yfd.monitor.media.zlm.event; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; + +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.service.IMediaNodeServerService; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.ZLMServerConfig; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service("zlm") +public class ZLMMediaNodeServerService implements IMediaNodeServerService { + + private final static Logger logger = LoggerFactory.getLogger(ZLMMediaNodeServerService.class); + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private ZLMServerFactory zlmServerFactory; + + @Value("${sip.ip}") + private String sipIp; + + @Override + public int createRTPServer(MediaServerItem mediaServer, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean disableAudio, Boolean reUsePort, Integer tcpMode) { + return zlmServerFactory.createRTPServer(mediaServer, streamId, ssrc, port, onlyAuto, reUsePort, tcpMode); + } + + @Override + public void closeRtpServer(MediaServerItem mediaServer, String streamId) { + zlmServerFactory.closeRtpServer(mediaServer, streamId); + } + + @Override + public void closeRtpServer(MediaServerItem mediaServer, String streamId, CommonCallback callback) { + zlmServerFactory.closeRtpServer(mediaServer, streamId, callback); + } + + @Override + public void closeStreams(MediaServerItem mediaServer, String app, String stream) { + zlmresTfulUtils.closeStreams(mediaServer, app, stream); + } + + @Override + public Boolean updateRtpServerSSRC(MediaServerItem mediaServer, String streamId, String ssrc) { + return zlmServerFactory.updateRtpServerSSRC(mediaServer, streamId, ssrc); + } + + @Override + public boolean checkNodeId(MediaServerItem mediaServer) { + if (mediaServer == null) { + return false; + } + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer); + if (responseJSON != null) { + JSONArray data = responseJSON.getJSONArray("data"); + if (data != null && !data.isEmpty()) { + ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); + return zlmServerConfig.getGeneralMediaServerId().equals(mediaServer.getId()); + }else { + return false; + } + + }else { + return false; + } + } + + @Override + public void online(MediaServerItem mediaServer) { + + } + + @Override + public MediaServerItem checkMediaServer(String ip, int port, String secret) { + MediaServerItem mediaServer = new MediaServerItem(); + mediaServer.setIp(ip); + mediaServer.setHttpPort(port); +// mediaServer.setFlvPort(port); +// mediaServer.setWsFlvPort(port); + mediaServer.setSecret(secret); + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServer); + if (responseJSON == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败"); + } + JSONArray data = responseJSON.getJSONArray("data"); + if (data == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败"); + } + ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); + if (zlmServerConfig == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败"); + } + mediaServer.setId(zlmServerConfig.getGeneralMediaServerId()); + mediaServer.setHttpSSlPort(zlmServerConfig.getHttpPort()); +// mediaServer.setFlvSSLPort(zlmServerConfig.getHttpPort()); +// mediaServer.setWsFlvSSLPort(zlmServerConfig.getHttpPort()); + mediaServer.setRtmpPort(zlmServerConfig.getRtmpPort()); + mediaServer.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort()); + mediaServer.setRtspPort(zlmServerConfig.getRtspPort()); + mediaServer.setRtspSSLPort(zlmServerConfig.getRtspSSlport()); + mediaServer.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); + mediaServer.setStreamIp(ip); + + mediaServer.setHookIp(sipIp.split(",")[0]); + mediaServer.setSdpIp(ip); +// mediaServer.setType("zlm"); + return mediaServer; + } + + @Override + public boolean stopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc) { + Map param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + if (!ObjectUtils.isEmpty(ssrc)) { + param.put("ssrc", ssrc); + } + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param); + if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { + logger.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); + } + return true; + + } + + @Override + public boolean initStopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc) { + Map param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", app); + param.put("stream", stream); + if (!ObjectUtils.isEmpty(ssrc)) { + param.put("ssrc", ssrc); + } + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param); + if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { + logger.error("停止发流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + return false; + } + return true; + } + + @Override + public boolean deleteRecordDirectory(MediaServerItem mediaServer, String app, String stream, String date, String fileName) { + logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件, server: {} {}:{}->{}/{}", mediaServer.getId(), app, stream, date, fileName); + JSONObject jsonObject = zlmresTfulUtils.deleteRecordDirectory(mediaServer, app, + stream, date, fileName); + if (jsonObject.getInteger("code") == 0) { + return true; + }else { + logger.info("[zlm-deleteRecordDirectory] 删除磁盘文件错误, server: {} {}:{}->{}/{}, 结果: {}", mediaServer.getId(), app, stream, date, fileName, jsonObject); + return false; + } + } + + @Override + public List getMediaList(MediaServerItem mediaServer, String app, String stream, String callId) { + List streamInfoList = new ArrayList<>(); + JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaServer, app, stream); + if (mediaList != null) { + if (mediaList.getInteger("code") == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data == null) { + return null; + } + JSONObject mediaJSON = data.getJSONObject(0); + MediaInfo mediaInfo = MediaInfo.getInstance(mediaJSON, mediaServer); + StreamInfo streamInfo = getStreamInfoByAppAndStream(mediaServer, app, stream, mediaInfo, callId, true); + if (streamInfo != null) { + streamInfoList.add(streamInfo); + } + } + } + return streamInfoList; + } + + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServer, String app, String stream, MediaInfo mediaInfo, String callId, boolean isPlay) { + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setStream(stream); + streamInfoResult.setApp(app); + String addr = mediaServer.getStreamIp(); + streamInfoResult.setIp(addr); + streamInfoResult.setMediaServerId(mediaServer.getId()); + String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; + streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam); + String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam); +// streamInfoResult.setFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile); +// streamInfoResult.setWsFlv(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), flvFile); + streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); +// streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay); + +// streamInfoResult.setMediaInfo(mediaInfo); +// streamInfoResult.setOriginType(mediaInfo.getOriginType()); + return streamInfoResult; + } + + @Override + public Boolean connectRtpServer(MediaServerItem mediaServer, String address, int port, String stream) { + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServer, address, port, stream); + logger.info("[TCP主动连接对方] 结果: {}", jsonObject); + return jsonObject.getInteger("code") == 0; + } + + @Override + public void getSnap(MediaServerItem mediaServer, String streamUrl, int timeoutSec, int expireSec, String path, String fileName) throws IOException { + zlmresTfulUtils.getSnap(mediaServer, streamUrl, timeoutSec, expireSec, path, fileName); + } + + @Override + public MediaInfo getMediaInfo(MediaServerItem mediaServer, String app, String stream) { + JSONObject jsonObject = zlmresTfulUtils.getMediaInfo(mediaServer, app, "rtsp", stream); + if (jsonObject.getInteger("code") != 0) { + return null; + } + return MediaInfo.getInstance(jsonObject, mediaServer); + } + + @Override + public Boolean pauseRtpCheck(MediaServerItem mediaServer, String streamKey) { + JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServer, streamKey); + return jsonObject.getInteger("code") == 0; + } + + @Override + public Boolean resumeRtpCheck(MediaServerItem mediaServer, String streamKey) { + JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServer, streamKey); + return jsonObject.getInteger("code") == 0; + } + + @Override + public String getFfmpegCmd(MediaServerItem mediaServer, String cmdKey) { + JSONObject jsonObject = zlmresTfulUtils.getMediaServerConfig(mediaServer); + if (jsonObject.getInteger("code") != 0) { + logger.warn("[getFfmpegCmd] 获取流媒体配置失败"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取流媒体配置失败"); + } + JSONArray dataArray = jsonObject.getJSONArray("data"); + JSONObject mediaServerConfig = dataArray.getJSONObject(0); + if (ObjectUtils.isEmpty(cmdKey)) { + cmdKey = "ffmpeg.cmd"; + } + return mediaServerConfig.getString(cmdKey); + } + + @Override + public WVPResult addFFmpegSource(MediaServerItem mediaServer, String srcUrl, String dstUrl, int timeoutMs, boolean enableAudio, boolean enableMp4, String ffmpegCmdKey) { + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaServer, srcUrl, dstUrl, timeoutMs+"", enableAudio, enableMp4, ffmpegCmdKey); + if (jsonObject.getInteger("code") != 0) { + logger.warn("[getFfmpegCmd] 添加FFMPEG代理失败"); + return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加FFMPEG代理失败"); + }else { + JSONObject data = jsonObject.getJSONObject("data"); + if (data == null) { + return WVPResult.fail(ErrorCode.ERROR100.getCode(), "代理结果异常: " + jsonObject); + }else { + return WVPResult.success(data.getString("key")); + } + } + } + + @Override + public WVPResult addStreamProxy(MediaServerItem mediaServer, String app, String stream, String url, boolean enableAudio, boolean enableMp4, String rtpType) { + JSONObject jsonObject = zlmresTfulUtils.addStreamProxy(mediaServer, app, stream, url, enableAudio, enableMp4, rtpType); + if (jsonObject.getInteger("code") != 0) { + return WVPResult.fail(ErrorCode.ERROR100.getCode(), "添加代理失败"); + }else { + JSONObject data = jsonObject.getJSONObject("data"); + if (data == null) { + return WVPResult.fail(ErrorCode.ERROR100.getCode(), "代理结果异常: " + jsonObject); + }else { + return WVPResult.success(data.getString("key")); + } + } + } + + @Override + public Boolean delFFmpegSource(MediaServerItem mediaServer, String streamKey) { + JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaServer, streamKey); + return jsonObject.getInteger("code") == 0; + } + + @Override + public Boolean delStreamProxy(MediaServerItem mediaServer, String streamKey) { + JSONObject jsonObject = zlmresTfulUtils.delStreamProxy(mediaServer, streamKey); + return jsonObject.getInteger("code") == 0; + } + + @Override + public Map getFFmpegCMDs(MediaServerItem mediaServer) { + Map result = new HashMap<>(); + JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServer); + if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0 + && mediaServerConfigResuly.getJSONArray("data").size() > 0){ + JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0); + + for (String key : mediaServerConfig.keySet()) { + if (key.startsWith("ffmpeg.cmd")){ + result.put(key, mediaServerConfig.getString(key)); + } + } + } + return result; + } + + @Override + public void startSendRtpPassive(MediaServerItem mediaServer, SendRtpItem sendRtpItem, Integer timeout) { + Map param = new HashMap<>(12); + param.put("vhost","__defaultVhost__"); + param.put("app", sendRtpItem.getApp()); + param.put("stream", sendRtpItem.getStreamId()); + param.put("ssrc", sendRtpItem.getSsrc()); + param.put("src_port", sendRtpItem.getLocalPort()); + param.put("pt", sendRtpItem.getPt()); + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); + param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1"); +// param.put("recv_stream_id", sendRtpItem.getReceiveStream()); + if (timeout != null) { + param.put("close_delay_ms", timeout); + } + if (!sendRtpItem.isTcp()) { + // 开启rtcp保活 + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0"); + } + if (!sendRtpItem.isTcpActive()) { + param.put("dst_url",sendRtpItem.getIp()); + param.put("dst_port", sendRtpItem.getPort()); + } + + JSONObject jsonObject = zlmServerFactory.startSendRtpPassive(mediaServer, param, null); + if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { + logger.error("启动监听TCP被动推流失败: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); + } + logger.info("调用ZLM-TCP被动推流接口, 结果: {}", jsonObject); + logger.info("启动监听TCP被动推流成功[ {}/{} ],{}->{}:{}, " , sendRtpItem.getApp(), sendRtpItem.getStreamId(), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); + } + + @Override + public void startSendRtpStream(MediaServerItem mediaServer, SendRtpItem sendRtpItem) { + Map param = new HashMap<>(12); + param.put("vhost", "__defaultVhost__"); + param.put("app", sendRtpItem.getApp()); + param.put("stream", sendRtpItem.getStreamId()); + param.put("ssrc", sendRtpItem.getSsrc()); + param.put("src_port", sendRtpItem.getLocalPort()); + param.put("pt", sendRtpItem.getPt()); + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); + param.put("is_udp", sendRtpItem.isTcp() ? "0" : "1"); + if (!sendRtpItem.isTcp()) { + // udp模式下开启rtcp保活 + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp() ? "1" : "0"); + } + param.put("dst_url", sendRtpItem.getIp()); + param.put("dst_port", sendRtpItem.getPort()); + JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServer, param); + if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { + throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMOfflineEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMOfflineEvent.java new file mode 100644 index 0000000..afa30ee --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMOfflineEvent.java @@ -0,0 +1,11 @@ +package com.yfd.monitor.media.zlm.event; + +/** + * zlm离线事件类 + */ +public class ZLMOfflineEvent extends ZLMEventAbstract { + + public ZLMOfflineEvent(Object source) { + super(source); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMOnlineEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMOnlineEvent.java new file mode 100644 index 0000000..f22fc77 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMOnlineEvent.java @@ -0,0 +1,11 @@ +package com.yfd.monitor.media.zlm.event; + +/** + * zlm在线事件 + */ +public class ZLMOnlineEvent extends ZLMEventAbstract { + + public ZLMOnlineEvent(Object source) { + super(source); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMServerFactory.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMServerFactory.java new file mode 100644 index 0000000..a488169 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMServerFactory.java @@ -0,0 +1,278 @@ +package com.yfd.monitor.media.zlm.event; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.zlm.SendRtpPortManager; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class ZLMServerFactory { + + private Logger logger = LoggerFactory.getLogger("ZLMServerFactory"); + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private UserSetting userSetting; + +// @Autowired +// private SendRtpPortManager sendRtpPortManager; + + + /** + * 开启rtpServer + * @param mediaServerItem zlm服务实例 + * @param streamId 流Id + * @param ssrc ssrc + * @param port 端口, 0/null为使用随机 + * @param reUsePort 是否重用端口 + * @param tcpMode 0/null udp 模式,1 tcp 被动模式, 2 tcp 主动模式。 + * @return + */ + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, long ssrc, Integer port, Boolean onlyAuto, Boolean reUsePort, Integer tcpMode) { + int result = -1; + // 查询此rtp server 是否已经存在 + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, streamId); + logger.info(JSONObject.toJSONString(rtpInfo)); + if(rtpInfo.getInteger("code") == 0){ + if (rtpInfo.getBoolean("exist")) { + result = rtpInfo.getInteger("local_port"); + if (result == 0) { + // 此时说明rtpServer已经创建但是流还没有推上来 + // 此时重新打开rtpServer + Map param = new HashMap<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerItem, param); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + return createRTPServer(mediaServerItem, streamId, ssrc, port,onlyAuto, reUsePort, tcpMode); + }else { + logger.warn("[开启rtpServer], 重启RtpServer错误"); + } + } + } + return result; + } + }else if(rtpInfo.getInteger("code") == -2){ + return result; + } + + Map param = new HashMap<>(); + + if (tcpMode == null) { + tcpMode = 0; + } + param.put("tcp_mode", tcpMode); + param.put("stream_id", streamId); + if (reUsePort != null) { + param.put("re_use_port", reUsePort?"1":"0"); + } + // 推流端口设置0则使用随机端口 + if (port == null) { + param.put("port", 0); + }else { + param.put("port", port); + } + if (onlyAuto != null) { + param.put("only_audio", onlyAuto?"1":"0"); + } + if (ssrc != 0) { + param.put("ssrc", ssrc); + } + + JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); + logger.info(JSONObject.toJSONString(openRtpServerResultJson)); + if (openRtpServerResultJson != null) { + if (openRtpServerResultJson.getInteger("code") == 0) { + result= openRtpServerResultJson.getInteger("port"); + }else { + logger.error("创建RTP Server 失败 {}: ", openRtpServerResultJson.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port")); + } + return result; + } + + public boolean closeRtpServer(MediaServerItem serverItem, String streamId) { + boolean result = false; + if (serverItem !=null){ + Map param = new HashMap<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param); + logger.info("关闭RTP Server " + jsonObject); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + result = jsonObject.getInteger("hit") >= 1; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + } + } + return result; + } + + public void closeRtpServer(MediaServerItem serverItem, String streamId, CommonCallback callback) { + if (serverItem == null) { + callback.run(false); + return; + } + Map param = new HashMap<>(); + param.put("stream_id", streamId); + zlmresTfulUtils.closeRtpServer(serverItem, param, jsonObject -> { + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + callback.run(jsonObject.getInteger("hit") == 1); + return; + }else { + logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg")); + } + }else { + // 检查ZLM状态 + logger.error("关闭RTP Server 失败: 请检查ZLM服务"); + } + callback.run(false); + }); + + + } + + + /** + * 调用zlm RESTFUL API —— startSendRtp + */ + public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { + return zlmresTfulUtils.startSendRtp(mediaServerItem, param); + } + + /** + * 调用zlm RESTFUL API —— startSendRtpPassive + */ + public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Mapparam) { + return zlmresTfulUtils.startSendRtpPassive(mediaServerItem, param); + } + + public JSONObject startSendRtpPassive(MediaServerItem mediaServerItem, Mapparam, ZLMRESTfulUtils.RequestCallback callback) { + return zlmresTfulUtils.startSendRtpPassive(mediaServerItem, param, callback); + } + + /** + * 查询待转推的流是否就绪 + */ + public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId); + if (mediaInfo == null || (mediaInfo.getInteger("code") == -2)) { + return null; + } + return (mediaInfo.getInteger("code") == 0 + && mediaInfo.getJSONArray("data") != null + && mediaInfo.getJSONArray("data").size() > 0); + } + + /** + * 查询转推的流是否有其它观看者 + * @param streamId + * @return + */ + public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) { + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId); + if (mediaInfo == null) { + return 0; + } + Integer code = mediaInfo.getInteger("code"); + if (code < 0) { + logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); + return -1; + } + if ( code == 0 && mediaInfo.getBoolean("online") != null && ! mediaInfo.getBoolean("online")) { + logger.warn("查询流({}/{})是否有其它观看者时得到: {}", app, streamId, mediaInfo.getString("msg")); + return -1; + } + return mediaInfo.getInteger("totalReaderCount"); + } + + public JSONObject startSendRtp(MediaServerItem mediaInfo, SendRtpItem sendRtpItem) { + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; + logger.info("rtp/{}开始推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); + Map param = new HashMap<>(12); + param.put("vhost","__defaultVhost__"); + param.put("app",sendRtpItem.getApp()); + param.put("stream",sendRtpItem.getStreamId()); + param.put("ssrc", sendRtpItem.getSsrc()); + param.put("src_port", sendRtpItem.getLocalPort()); + param.put("pt", sendRtpItem.getPt()); + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); + if (!sendRtpItem.isTcp()) { + // udp模式下开启rtcp保活 + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0"); + } + + if (mediaInfo == null) { + return null; + } + // 如果是非严格模式,需要关闭端口占用 + JSONObject startSendRtpStreamResult = null; + if (sendRtpItem.getLocalPort() != 0) { + if (sendRtpItem.isTcpActive()) { + startSendRtpStreamResult = startSendRtpPassive(mediaInfo, param); + System.out.println(JSON.toJSON(param)); + }else { + param.put("is_udp", is_Udp); + param.put("dst_url", sendRtpItem.getIp()); + param.put("dst_port", sendRtpItem.getPort()); + startSendRtpStreamResult = startSendRtpStream(mediaInfo, param); + } + }else { + if (sendRtpItem.isTcpActive()) { + startSendRtpStreamResult = startSendRtpPassive(mediaInfo, param); + }else { + param.put("is_udp", is_Udp); + param.put("dst_url", sendRtpItem.getIp()); + param.put("dst_port", sendRtpItem.getPort()); + startSendRtpStreamResult = startSendRtpStream(mediaInfo, param); + } + } + return startSendRtpStreamResult; + } + + public Boolean updateRtpServerSSRC(MediaServerItem mediaServerItem, String streamId, String ssrc) { + boolean result = false; + JSONObject jsonObject = zlmresTfulUtils.updateRtpServerSSRC(mediaServerItem, streamId, ssrc); + if (jsonObject == null) { + logger.error("[更新RTPServer] 失败: 请检查ZLM服务"); + } else if (jsonObject.getInteger("code") == 0) { + result= true; + logger.info("[更新RTPServer] 成功"); + } else { + logger.error("[更新RTPServer] 失败: {}, streamId:{},ssrc:{}->\r\n{}",jsonObject.getString("msg"), + streamId, ssrc, jsonObject); + } + return result; + } + + public JSONObject stopSendRtpStream(MediaServerItem mediaServerItem, SendRtpItem sendRtpItem) { + Map param = new HashMap<>(); + param.put("vhost", "__defaultVhost__"); + param.put("app", sendRtpItem.getApp()); + param.put("stream", sendRtpItem.getStreamId()); + param.put("ssrc", sendRtpItem.getSsrc()); + return zlmresTfulUtils.stopSendRtp(mediaServerItem, param); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMStatusEventListener.java b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMStatusEventListener.java new file mode 100644 index 0000000..278da08 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/media/zlm/event/ZLMStatusEventListener.java @@ -0,0 +1,59 @@ +package com.yfd.monitor.media.zlm.event; + +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.service.IStreamPushService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + + +/** + * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源: + * 1、设备主动注销,发送注销指令 + * 2、设备未知原因离线,心跳超时 + * + * @date: 2020年5月6日 下午1:51:23 + */ +@Component +public class ZLMStatusEventListener { + + private final static Logger logger = LoggerFactory.getLogger(ZLMStatusEventListener.class); + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IStreamProxyService streamProxyService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IPlayService playService; + + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(ZLMOnlineEvent event) { + logger.info("[ZLM] 上线 ID:" + event.getMediaServerId()); + streamPushService.zlmServerOnline(event.getMediaServerId()); + streamProxyService.zlmServerOnline(event.getMediaServerId()); + playService.zlmServerOnline(event.getMediaServerId()); + } + + @Async("taskExecutor") + @EventListener + public void onApplicationEvent(ZLMOfflineEvent event) { + + logger.info("[ZLM] 离线,ID:" + event.getMediaServerId()); + // 处理ZLM离线 + mediaServerService.zlmServerOffline(event.getMediaServerId()); + streamProxyService.zlmServerOffline(event.getMediaServerId()); + streamPushService.zlmServerOffline(event.getMediaServerId()); + playService.zlmServerOffline(event.getMediaServerId()); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceAlarmService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceAlarmService.java new file mode 100644 index 0000000..dfbc080 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceAlarmService.java @@ -0,0 +1,43 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 报警相关业务处理 + */ +public interface IDeviceAlarmService { + + /** + * 根据多个添加获取报警列表 + * @param page 当前页 + * @param count 每页数量 + * @param deviceId 设备id + * @param alarmPriority 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- + * @param alarmMethod 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, + * 7其他报警;可以为直接组合如12为电话报警或 设备报警- + * @param alarmType 报警类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 报警列表 + */ + PageInfo getAllAlarm(int page, int count, String deviceId, String alarmPriority, String alarmMethod, + String alarmType, String startTime, String endTime); + + /** + * 添加一个报警 + * @param deviceAlarm 添加报警 + */ + void add(DeviceAlarm deviceAlarm); + + /** + * 清空时间以前的报警 + * @param id 数据库id + * @param deviceIdList 制定需要清理的设备id + * @param time 不写时间则清空所有时间的 + */ + int clearAlarmBeforeTime(Integer id, List deviceIdList, String time); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceChannelService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceChannelService.java new file mode 100644 index 0000000..7613e17 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceChannelService.java @@ -0,0 +1,89 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; + +import java.util.List; + +/** + * 国标通道业务类 + */ +public interface IDeviceChannelService { + + /** + * 更新gps信息 + */ + DeviceChannel updateGps(DeviceChannel deviceChannel, Device device); + + /** + * 添加设备通道 + * + * @param deviceId 设备id + * @param channel 通道 + */ + void updateChannel(String deviceId, DeviceChannel channel); + + /** + * 批量添加设备通道 + * + * @param deviceId 设备id + * @param channels 多个通道 + */ + int updateChannels(String deviceId, List channels); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); + + /** + * 查询所有未分配的通道 + * @param platformId + * @return + */ + List queryAllChannelList(String platformId); + + /** + * 数据位置信息格式处理 + */ + boolean updateAllGps(Device device); + + /** + * 查询通道所属的设备 + */ + List getDeviceByChannelId(String channelId); + + /** + * 批量删除通道 + * @param deleteChannelList 待删除的通道列表 + */ + int deleteChannels(List deleteChannelList); + + /** + * 批量上线 + */ + int channelsOnline(List channels); + + /** + * 批量下线 + */ + int channelsOffline(List channels); + + /** + * 获取一个通道 + */ + DeviceChannel getOne(String deviceId, String channelId); + + /** + * 直接批量更新通道 + */ + void batchUpdateChannel(List channels); + + /** + * 直接批量添加 + */ + void batchAddChannel(List deviceChannels); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceService.java new file mode 100644 index 0000000..f4f54e7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IDeviceService.java @@ -0,0 +1,170 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.yfd.monitor.gdw2019.bean.SyncStatus; +import com.yfd.monitor.vmanager.bean.BaseTree; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; + +import java.util.List; + +/** + * 设备相关业务处理 + */ +public interface IDeviceService { + + /** + * 设备上线 + * @param device 设备信息 + */ + void online(Device device, SipTransactionInfo sipTransactionInfo); + + /** + * 设备下线 + * @param deviceId 设备编号 + */ + void offline(String deviceId, String reason); + + /** + * 添加目录订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean addCatalogSubscribe(Device device); + + /** + * 移除目录订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean removeCatalogSubscribe(Device device); + + /** + * 添加移动位置订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean addMobilePositionSubscribe(Device device); + + /** + * 移除移动位置订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean removeMobilePositionSubscribe(Device device); + + /** + * 移除移动位置订阅 + * @param deviceId 设备ID + * @return 同步状态 + */ + SyncStatus getChannelSyncStatus(String deviceId); + + /** + * 查看是否仍在同步 + * @param deviceId 设备ID + * @return 布尔 + */ + Boolean isSyncRunning(String deviceId); + + /** + * 通道同步 + * @param device 设备信息 + */ + void sync(Device device); + + /** + * 查询设备信息 + * @param deviceId 设备编号 + * @return 设备信息 + */ + Device getDevice(String deviceId); + + /** + * 获取所有在线设备 + * @return 设备列表 + */ + List getAllOnlineDevice(); + + /** + * 判断是否注册已经失效 + * @param device 设备信息 + * @return 布尔 + */ + boolean expire(Device device); + + /** + * 检查设备状态 + * @param device 设备信息 + */ + void checkDeviceStatus(Device device); + + /** + * 根据IP和端口获取设备信息 + * @param host IP + * @param port 端口 + * @return 设备信息 + */ + Device getDeviceByHostAndPort(String host, int port); + + /** + * 更新设备 + * @param device 设备信息 + */ + void updateDevice(Device device); + + /** + * 树形查询接口 + * @param deviceId 设备ID + * @param parentId 父ID + * @param onlyCatalog 只获取目录 + * @return + */ + List> queryVideoDeviceTree(String deviceId, String parentId, boolean onlyCatalog); + + /** + * 查询树节点下的通道 + * @param deviceId 设备ID + * @param parentId 父ID + * @return + */ + List queryVideoDeviceInTreeNode(String deviceId, String parentId); + + /** + * 检查设备编号是否已经存在 + * @param deviceId 设备编号 + * @return + */ + boolean isExist(String deviceId); + + /** + * 添加设备 + * @param device + */ + void addDevice(Device device); + + /** + * 页面表单更新设备信息 + * @param device + */ + void updateCustomDevice(Device device); + + /** + * 删除设备 + * @param deviceId + * @return + */ + boolean delete(String deviceId); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); + + /** + * 获取所有设备 + */ + List getAll(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IGbStreamService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IGbStreamService.java new file mode 100644 index 0000000..e9151ac --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IGbStreamService.java @@ -0,0 +1,74 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +/** + * 级联国标平台关联流业务接口 + */ +public interface IGbStreamService { + + /** + * 分页获取所有 + * @param page + * @param count + * @return + */ + PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId,String query,String mediaServerId); + + + /** + * 移除 + * @param app + * @param stream + */ + void del(String app, String stream); + + /** + * 保存国标关联 + * @param gbStreams + */ + boolean addPlatformInfo(List gbStreams, String platformId, String catalogId); + + /** + * 移除国标关联 + * @param gbStreams + * @param platformId + */ + boolean delPlatformInfo(String platformId, List gbStreams); + + DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, ParentPlatform platform); + + void sendCatalogMsg(GbStream gbStream, String type); + void sendCatalogMsgs(List gbStreams, String type); + + /** + * 修改gbId或name + * @param streamPushItemForUpdate + * @return + */ + int updateGbIdOrName(List streamPushItemForUpdate); + + DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform); + + /** + * 查询所有未分配的通道 + * @param platformId + * @return + */ + List getAllGBChannels(String platformId); + + /** + * 移除所有关联的通道 + * @param platformId + * @param catalogId + */ + void delAllPlatformInfo(String platformId, String catalogId); + + List getGbChannelWithGbid(String gbId); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IInviteStreamService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IInviteStreamService.java new file mode 100644 index 0000000..b392d48 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IInviteStreamService.java @@ -0,0 +1,87 @@ +package com.yfd.monitor.service; + + +import com.yfd.monitor.common.InviteInfo; +import com.yfd.monitor.common.InviteSessionType; +import com.yfd.monitor.service.bean.ErrorCallback; + +/** + * 记录国标点播的状态,包括实时预览,下载,录像回放 + */ +public interface IInviteStreamService { + + /** + * 更新点播的状态信息 + */ + void updateInviteInfo(InviteInfo inviteInfo); + + InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream); + + /** + * 获取点播的状态信息 + */ + InviteInfo getInviteInfo(InviteSessionType type, + String deviceId, + String channelId, + String stream); + + /** + * 移除点播的状态信息 + */ + void removeInviteInfo(InviteSessionType type, + String deviceId, + String channelId, + String stream); + /** + * 移除点播的状态信息 + */ + void removeInviteInfo(InviteInfo inviteInfo); + /** + * 移除点播的状态信息 + */ + void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId); + + /** + * 获取点播的状态信息 + */ + InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, + String deviceId, + String channelId); + + /** + * 获取点播的状态信息 + */ + InviteInfo getInviteInfoByStream(InviteSessionType type, String stream); + + + /** + * 添加一个invite回调 + */ + void once(InviteSessionType type, String deviceId, String channelId, String stream, ErrorCallback callback); + + /** + * 调用一个invite回调 + */ + void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data); + + /** + * 清空一个设备的所有invite信息 + */ + void clearInviteInfo(String deviceId); + + /** + * 统计同一个zlm下的国标收流个数 + */ + int getStreamInfoCount(String mediaServerId); + + + /** + * 获取MediaServer下的流信息 + */ + InviteInfo getInviteInfoBySSRC(String ssrc); + + /** + * 更新ssrc + */ + InviteInfo updateInviteInfoForSSRC(InviteInfo inviteInfo, String ssrcInResponse); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/ILogService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/ILogService.java new file mode 100644 index 0000000..f641e6c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/ILogService.java @@ -0,0 +1,34 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.storager.dao.dto.LogDto; +import com.github.pagehelper.PageInfo; + +/** + * 系统日志 + */ +public interface ILogService { + + /** + * 查询日志 + * @param page 当前页 + * @param count 每页数量 + * @param query 搜索内容 + * @param type 类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 日志列表 + */ + PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime); + + /** + * 添加日志 + * @param logDto 日志 + */ + void add(LogDto logDto); + + /** + * 清空 + */ + int clear(); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IMediaServerService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IMediaServerService.java new file mode 100644 index 0000000..99b6244 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IMediaServerService.java @@ -0,0 +1,117 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.ZLMServerConfig; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.ServerKeepaliveData; +import com.yfd.monitor.service.bean.MediaServerLoad; +import com.yfd.monitor.service.bean.SSRCInfo; + +import java.util.List; + +/** + * 媒体服务节点 + */ +public interface IMediaServerService { + + List getAll(); + + List getAllFromDatabase(); + + List getAllOnline(); + + MediaServerItem getOne(String generalMediaServerId); + + void syncCatchFromDatabase(); + + /** + * 新的节点加入 + * @param zlmServerConfig + * @return + */ + void zlmServerOnline(ZLMServerConfig zlmServerConfig); + + /** + * 节点离线 + * @param mediaServerId + * @return + */ + void zlmServerOffline(String mediaServerId); + + MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist); + + void setZLMConfig(MediaServerItem mediaServerItem, boolean restart); + + void updateVmServer(List mediaServerItemList); + + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, + boolean isPlayback, Integer port, Boolean reUsePort, Integer tcpMode); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback callback); + + void closeRTPServer(String mediaServerId, String streamId); + + void clearRTPServer(MediaServerItem mediaServerItem); + + void update(MediaServerItem mediaSerItem); + + void addCount(String mediaServerId); + + void removeCount(String mediaServerId); + + void releaseSsrc(String mediaServerItemId, String ssrc); + + void clearMediaServerForOnline(); + + void add(MediaServerItem mediaSerItem); + + int addToDatabase(MediaServerItem mediaSerItem); + + int updateToDatabase(MediaServerItem mediaSerItem); + + void resetOnlineServerItem(MediaServerItem serverItem); + + MediaServerItem checkMediaServer(String ip, int port, String secret); + + boolean checkMediaRecordServer(String ip, int port); + + void delete(String id); + + void deleteDb(String id); + + MediaServerItem getDefaultMediaServer(); + + void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data); + + boolean checkRtpServer(MediaServerItem mediaServerItem, String rtp, String stream); + + /** + * 获取负载信息 + * @return + */ + MediaServerLoad getLoad(MediaServerItem mediaServerItem); + + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay); + + boolean stopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc); + + Boolean isStreamReady(MediaServerItem mediaServer, String rtp, String streamId); + + void startSendRtpPassive(MediaServerItem mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout); + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, MediaInfo mediaInfo, String callId); + + SendRtpItem createSendRtpItem(MediaServerItem mediaServerItem, String addressStr, int port, String ssrc, String requesterId, String deviceId, String channelId, boolean mediaTransmissionTCP, boolean rtcp); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IMediaService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IMediaService.java new file mode 100644 index 0000000..b59441d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IMediaService.java @@ -0,0 +1,43 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; + +/** + * 媒体信息业务 + */ +public interface IMediaService { + + /** + * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority); + + + /** + * 根据应用名和流ID获取播放地址, 通过zlm接口检查是否存在, 返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks, String callId); + + /** + * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 + * @param app + * @param stream + * @return + */ + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IPlatformChannelService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IPlatformChannelService.java new file mode 100644 index 0000000..5443d83 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IPlatformChannelService.java @@ -0,0 +1,28 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; + +import java.util.List; + +/** + * 平台关联通道管理 + */ +public interface IPlatformChannelService { + + /** + * 更新目录下的通道 + * @param platformId 平台编号 + * @param channelReduces 通道信息 + * @param catalogId 目录编号 + * @return + */ + int updateChannelForGB(String platformId, List channelReduces, String catalogId); + + /** + * 移除目录下的所有通道 + * @param platformId + * @param catalogId + * @return + */ + int delAllChannelForGB(String platformId, String catalogId); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IPlatformService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IPlatformService.java new file mode 100644 index 0000000..4bed7d8 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IPlatformService.java @@ -0,0 +1,65 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SipTransactionInfo; +import com.github.pagehelper.PageInfo; + +/** + * 国标平台的业务类 + */ +public interface IPlatformService { + + ParentPlatform queryPlatformByServerGBId(String platformGbId); + + /** + * 分页获取上级平台 + * @param page + * @param count + * @return + */ + PageInfo queryParentPlatformList(int page, int count); + + /** + * 添加级联平台 + * @param parentPlatform 级联平台 + */ + boolean add(ParentPlatform parentPlatform); + + /** + * 添加级联平台 + * @param parentPlatform 级联平台 + */ + boolean update(ParentPlatform parentPlatform); + + /** + * 平台上线 + * @param parentPlatform 平台信息 + */ + void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo); + + /** + * 平台离线 + * @param parentPlatform 平台信息 + */ + void offline(ParentPlatform parentPlatform, boolean stopRegisterTask); + + /** + * 向上级平台发起注册 + * @param parentPlatform + */ + void login(ParentPlatform parentPlatform); + + /** + * 向上级平台主动发起注销 + * @param parentPlatform + */ + void logout(ParentPlatform parentPlatform); + + /** + * 向上级平台发送位置订阅 + * @param platformId 平台 + */ + void sendNotifyMobilePosition(String platformId); + + void addSimulatedSubscribeInfo(ParentPlatform parentPlatform); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IPlayService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IPlayService.java new file mode 100644 index 0000000..6e9aa25 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IPlayService.java @@ -0,0 +1,71 @@ +package com.yfd.monitor.service; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.exception.ServiceException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.InviteStreamCallback; +import com.yfd.monitor.gdw2019.bean.InviteStreamInfo; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.bean.ErrorCallback; +import com.yfd.monitor.service.bean.InviteTimeOutCallback; +import com.yfd.monitor.service.bean.PlayBackCallback; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.vmanager.bean.AudioBroadcastResult; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import com.yfd.monitor.vmanager.gdw2019.play.bean.AudioBroadcastEvent; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * 点播处理 + */ +public interface IPlayService { + StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId); + void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId); + + void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + InviteTimeOutCallback timeoutCallback); + void play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent, Runnable timeoutCallback); +// SSRCInfo play(MediaServerItem mediaServerItem, String deviceId, String channelId, String ssrc, ErrorCallback callback); + + DeferredResult> play_subStream(MediaServerItem mediaServerItem, String deviceId, String channelId); + MediaServerItem getNewMediaServerItem(Device device); + + /** + * 获取包含assist服务的节点 + */ + MediaServerItem getNewMediaServerItemHasAssist(Device device); + + void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String toString); + + void playBack(String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback); + void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack); + + void zlmServerOffline(String mediaServerId); + + void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback); + void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack); + + StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); + + void zlmServerOnline(String mediaServerId); + + void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + + void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode); + + void stopAudioBroadcast(String deviceId, String channelId); + boolean audioBroadcastCmd(Device device, String channelId, MediaServerItem mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException; + void talkCmd(Device device, String channelId, MediaServerItem mediaServerItem, String stream, AudioBroadcastEvent event); + void stopTalk(Device device, String channelId, Boolean streamIsReady); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IRecordInfoServer.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IRecordInfoServer.java new file mode 100644 index 0000000..4b4b2ad --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IRecordInfoServer.java @@ -0,0 +1,8 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.storager.dao.dto.RecordInfo; +import com.github.pagehelper.PageInfo; + +public interface IRecordInfoServer { + PageInfo getRecordList(int page, int count); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IRoleService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IRoleService.java new file mode 100644 index 0000000..00a7227 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IRoleService.java @@ -0,0 +1,18 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.storager.dao.dto.Role; + +import java.util.List; + +public interface IRoleService { + + Role getRoleById(int id); + + int add(Role role); + + int delete(int id); + + List getAll(); + + int update(Role role); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IStreamProxyService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IStreamProxyService.java new file mode 100644 index 0000000..5a58025 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IStreamProxyService.java @@ -0,0 +1,113 @@ +package com.yfd.monitor.service; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.github.pagehelper.PageInfo; + +public interface IStreamProxyService { + + /** + * 保存视频代理 + * @param param + */ + StreamInfo save(StreamProxyItem param); + + /** + * 添加视频代理到zlm + * @param param + * @return + */ + JSONObject addStreamProxyToZlm(StreamProxyItem param); + + /** + * 从zlm移除视频代理 + * @param param + * @return + */ + JSONObject removeStreamProxyFromZlm(StreamProxyItem param); + + /** + * 分页查询 + * @param page + * @param count + * @return + */ + PageInfo getAll(Integer page, Integer count); + + /** + * 删除视频代理 + * @param app + * @param stream + */ + void del(String app, String stream); + + /** + * 启用视频代理 + * @param app + * @param stream + * @return + */ + boolean start(String app, String stream); + + /** + * 更新状态 + * @param status 状态 + * @param app + * @param stream + */ + int updateStatus(boolean status, String app, String stream); + + + + /** + * 停用用视频代理 + * @param app + * @param stream + * @return + */ + boolean stop(String app, String stream); + + /** + * 获取ffmpeg.cmd模板 + * @return + */ + JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem); + + /** + * 根据app与stream获取streamProxy + * @return + */ + StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId); + + + /** + * 新的节点加入 + * @param mediaServerId + * @return + */ + void zlmServerOnline(String mediaServerId); + + /** + * 节点离线 + * @param mediaServerId + * @return + */ + void zlmServerOffline(String mediaServerId); + + void clean(); + + /** + * 更新代理流 + */ + boolean updateStreamProxy(StreamProxyItem streamProxyItem); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IStreamPushService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IStreamPushService.java new file mode 100644 index 0000000..1a6c88f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IStreamPushService.java @@ -0,0 +1,115 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.service.bean.StreamPushItemFromRedis; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.github.pagehelper.PageInfo; + +import java.util.List; +import java.util.Map; + + +public interface IStreamPushService { + + List handleJSON(String json, MediaServerItem mediaServerItem); + + /** + * 将应用名和流ID加入国标关联 + * @param stream + * @return + */ + boolean saveToGB(GbStream stream); + + /** + * 将应用名和流ID移出国标关联 + * @param stream + * @return + */ + boolean removeFromGB(GbStream stream); + + /** + * 获取 + */ + PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId); + + List getPushList(String mediaSererId); + + StreamPushItem transform(OnStreamChangedHookParam item); + + StreamPushItem getPush(String app, String streamId); + + /** + * 停止一路推流 + * @param app 应用名 + * @param streamId 流ID + */ + boolean stop(String app, String streamId); + + /** + * 新的节点加入 + */ + void zlmServerOnline(String mediaServerId); + + /** + * 节点离线 + */ + void zlmServerOffline(String mediaServerId); + + /** + * 清空 + */ + void clean(); + + + boolean saveToRandomGB(); + + /** + * 批量添加 + */ + void batchAdd(List streamPushExcelDtoList); + + /** + * 中止多个推流 + */ + boolean batchStop(List streamPushItems); + + /** + * 导入时批量增加 + */ + void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll); + + /** + * 全部离线 + */ + void allStreamOffline(); + + /** + * 推流离线 + */ + void offline(List offlineStreams); + + /** + * 推流上线 + */ + void online(List onlineStreams); + + /** + * 增加推流 + */ + boolean add(StreamPushItem stream); + + /** + * 获取全部的app+Streanm 用于判断推流列表是新增还是修改 + * @return + */ + List getAllAppAndStream(); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/IUserService.java b/riis-monitor/src/main/java/com/yfd/monitor/service/IUserService.java new file mode 100644 index 0000000..747c9c3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/IUserService.java @@ -0,0 +1,29 @@ +package com.yfd.monitor.service; + +import com.yfd.monitor.storager.dao.dto.User; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +public interface IUserService { + + User getUser(String username, String password); + + boolean changePassword(int id, String password); + + User getUserByUsername(String username); + + int addUser(User user); + + int deleteUser(int id); + + List getAllUsers(); + + int updateUsers(User user); + + boolean checkPushAuthority(String callId, String sign); + + PageInfo getUsers(int page, int count); + + int changePushKey(int id, String pushKey); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/DownloadFileInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/DownloadFileInfo.java new file mode 100644 index 0000000..63396b4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/DownloadFileInfo.java @@ -0,0 +1,41 @@ +package com.yfd.monitor.service.bean; + +public class DownloadFileInfo { + + private String httpPath; + private String httpsPath; + private String httpDomainPath; + private String httpsDomainPath; + + public String getHttpPath() { + return httpPath; + } + + public void setHttpPath(String httpPath) { + this.httpPath = httpPath; + } + + public String getHttpsPath() { + return httpsPath; + } + + public void setHttpsPath(String httpsPath) { + this.httpsPath = httpsPath; + } + + public String getHttpDomainPath() { + return httpDomainPath; + } + + public void setHttpDomainPath(String httpDomainPath) { + this.httpDomainPath = httpDomainPath; + } + + public String getHttpsDomainPath() { + return httpsDomainPath; + } + + public void setHttpsDomainPath(String httpsDomainPath) { + this.httpsDomainPath = httpsDomainPath; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ErrorCallback.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ErrorCallback.java new file mode 100644 index 0000000..23b90a0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ErrorCallback.java @@ -0,0 +1,6 @@ +package com.yfd.monitor.service.bean; + +public interface ErrorCallback { + + void run(int code, String msg, T data); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/GPSMsgInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/GPSMsgInfo.java new file mode 100644 index 0000000..89bc9e4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/GPSMsgInfo.java @@ -0,0 +1,106 @@ +package com.yfd.monitor.service.bean; + +public class GPSMsgInfo { + + /** + * + */ + private String id; + + /** + * 经度 (必选) + */ + private double lng; + + /** + * 纬度 (必选) + */ + private double lat; + + /** + * 速度,单位:km/h (可选) + */ + private double speed; + + /** + * 产生通知时间, 时间格式: 2020-01-14T14:32:12 + */ + private String time; + + /** + * 方向,取值为当前摄像头方向与正北方的顺时针夹角,取值范围0°~360°,单位:(°)(可选) + */ + private String direction; + + /** + * 海拔高度,单位:m(可选) + */ + private String altitude; + + private boolean stored; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public double getLng() { + return lng; + } + + public void setLng(double lng) { + this.lng = lng; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getSpeed() { + return speed; + } + + public void setSpeed(double speed) { + this.speed = speed; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getDirection() { + return direction; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public String getAltitude() { + return altitude; + } + + public void setAltitude(String altitude) { + this.altitude = altitude; + } + + public boolean isStored() { + return stored; + } + + public void setStored(boolean stored) { + this.stored = stored; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/InviteErrorCode.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/InviteErrorCode.java new file mode 100644 index 0000000..e944ee7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/InviteErrorCode.java @@ -0,0 +1,37 @@ +package com.yfd.monitor.service.bean; + +/** + * 全局错误码 + */ +public enum InviteErrorCode { + SUCCESS(0, "成功"), + FAIL(-100, "失败"), + ERROR_FOR_SIGNALLING_TIMEOUT(-1, "信令超时"), + ERROR_FOR_STREAM_TIMEOUT(-2, "收流超时"), + ERROR_FOR_RESOURCE_EXHAUSTION(-3, "资源耗尽"), + ERROR_FOR_CATCH_DATA(-4, "缓存数据异常"), + ERROR_FOR_SIGNALLING_ERROR(-5, "收到信令错误"), + ERROR_FOR_STREAM_PARSING_EXCEPTIONS(-6, "流地址解析错误"), + ERROR_FOR_SDP_PARSING_EXCEPTIONS(-7, "SDP信息解析失败"), + ERROR_FOR_SSRC_UNAVAILABLE(-8, "SSRC不可用"), + ERROR_FOR_RESET_SSRC(-9, "重新设置收流信息失败"), + ERROR_FOR_SIP_SENDING_FAILED(-10, "命令发送失败"), + ERROR_FOR_ASSIST_NOT_READY(-11, "没有可用的assist服务"), + ERROR_FOR_PARAMETER_ERROR(-13, "参数异常"); + + private final int code; + private final String msg; + + InviteErrorCode(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/InviteTimeOutCallback.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/InviteTimeOutCallback.java new file mode 100644 index 0000000..0f0878d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/InviteTimeOutCallback.java @@ -0,0 +1,6 @@ +package com.yfd.monitor.service.bean; + +public interface InviteTimeOutCallback { + + void run(int code, String msg); // code: 0 sip超时, 1 收流超时 +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MediaServerLoad.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MediaServerLoad.java new file mode 100644 index 0000000..168e219 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MediaServerLoad.java @@ -0,0 +1,50 @@ +package com.yfd.monitor.service.bean; + +public class MediaServerLoad { + + private String id; + private int push; + private int proxy; + private int gbReceive; + private int gbSend; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getPush() { + return push; + } + + public void setPush(int push) { + this.push = push; + } + + public int getProxy() { + return proxy; + } + + public void setProxy(int proxy) { + this.proxy = proxy; + } + + public int getGbReceive() { + return gbReceive; + } + + public void setGbReceive(int gbReceive) { + this.gbReceive = gbReceive; + } + + public int getGbSend() { + return gbSend; + } + + public void setGbSend(int gbSend) { + this.gbSend = gbSend; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MessageForPushChannel.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MessageForPushChannel.java new file mode 100644 index 0000000..302e961 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MessageForPushChannel.java @@ -0,0 +1,128 @@ +package com.yfd.monitor.service.bean; + +/** + * 当上级平台 + */ +public class MessageForPushChannel { + /** + * 消息类型 + * 0 流注销 1 流注册 + */ + private int type; + + /** + * 流应用名 + */ + private String app; + + /** + * 流Id + */ + private String stream; + + /** + * 国标ID + */ + private String gbId; + + /** + * 请求的平台ID + */ + private String platFormId; + + /** + * 请求平台名称 + */ + private String platFormName; + + /** + * WVP服务ID + */ + private String serverId; + + /** + * 目标流媒体节点ID + */ + private String mediaServerId; + + + + public static MessageForPushChannel getInstance(int type, String app, String stream, String gbId, + String platFormId, String platFormName, String serverId, + String mediaServerId){ + MessageForPushChannel messageForPushChannel = new MessageForPushChannel(); + messageForPushChannel.setType(type); + messageForPushChannel.setGbId(gbId); + messageForPushChannel.setApp(app); + messageForPushChannel.setStream(stream); + messageForPushChannel.setMediaServerId(mediaServerId); + messageForPushChannel.setPlatFormId(platFormId); + messageForPushChannel.setPlatFormName(platFormName); + return messageForPushChannel; + } + + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getGbId() { + return gbId; + } + + public void setGbId(String gbId) { + this.gbId = gbId; + } + + public String getPlatFormId() { + return platFormId; + } + + public void setPlatFormId(String platFormId) { + this.platFormId = platFormId; + } + + public String getPlatFormName() { + return platFormName; + } + + public void setPlatFormName(String platFormName) { + this.platFormName = platFormName; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MessageForPushChannelResponse.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MessageForPushChannelResponse.java new file mode 100644 index 0000000..8353c7b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/MessageForPushChannelResponse.java @@ -0,0 +1,70 @@ +package com.yfd.monitor.service.bean; + +/** + * 当redis回复推流结果上级平台 + */ +public class MessageForPushChannelResponse { + /** + * 错误玛 + * 0 成功 1 失败 + */ + private int code; + /** + * 错误内容 + */ + private String msg; + + /** + * 流应用名 + */ + private String app; + + /** + * 流Id + */ + private String stream; + + + + public static MessageForPushChannelResponse getInstance(int code, String msg, String app, String stream){ + MessageForPushChannelResponse messageForPushChannel = new MessageForPushChannelResponse(); + messageForPushChannel.setCode(code); + messageForPushChannel.setMsg(msg); + messageForPushChannel.setApp(app); + messageForPushChannel.setStream(stream); + return messageForPushChannel; + } + + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PlayBackCallback.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PlayBackCallback.java new file mode 100644 index 0000000..d094b41 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PlayBackCallback.java @@ -0,0 +1,7 @@ +package com.yfd.monitor.service.bean; + +public interface PlayBackCallback { + + void call(PlayBackResult msg); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PlayBackResult.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PlayBackResult.java new file mode 100644 index 0000000..6abad3d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PlayBackResult.java @@ -0,0 +1,67 @@ +package com.yfd.monitor.service.bean; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; + +import java.util.EventObject; + + + +public class PlayBackResult { + private int code; + + private String msg; + private T data; + private MediaServerItem mediaServerItem; + private JSONObject response; + private SipSubscribe.EventResult event; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } + + public JSONObject getResponse() { + return response; + } + + public void setResponse(JSONObject response) { + this.response = response; + } + + public SipSubscribe.EventResult getEvent() { + return event; + } + + public void setEvent(SipSubscribe.EventResult event) { + this.event = event; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PushStreamStatusChangeFromRedisDto.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PushStreamStatusChangeFromRedisDto.java new file mode 100644 index 0000000..21713e0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/PushStreamStatusChangeFromRedisDto.java @@ -0,0 +1,40 @@ +package com.yfd.monitor.service.bean; + +import java.util.List; + +/** + * 收到redis通知修改推流通道状态 + */ +public class PushStreamStatusChangeFromRedisDto { + + private boolean setAllOffline; + + private List onlineStreams; + + private List offlineStreams; + + + public boolean isSetAllOffline() { + return setAllOffline; + } + + public void setSetAllOffline(boolean setAllOffline) { + this.setAllOffline = setAllOffline; + } + + public List getOnlineStreams() { + return onlineStreams; + } + + public void setOnlineStreams(List onlineStreams) { + this.onlineStreams = onlineStreams; + } + + public List getOfflineStreams() { + return offlineStreams; + } + + public void setOfflineStreams(List offlineStreams) { + this.offlineStreams = offlineStreams; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/RequestPushStreamMsg.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/RequestPushStreamMsg.java new file mode 100644 index 0000000..c79b2ee --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/RequestPushStreamMsg.java @@ -0,0 +1,169 @@ +package com.yfd.monitor.service.bean; + +/** + * redis消息:请求下级推送流信息 + */ +public class RequestPushStreamMsg { + + + /** + * 下级服务ID + */ + private String mediaServerId; + + /** + * 流ID + */ + private String app; + + /** + * 应用名 + */ + private String stream; + + /** + * 目标IP + */ + private String ip; + + /** + * 目标端口 + */ + private int port; + + /** + * ssrc + */ + private String ssrc; + + /** + * 是否使用TCP方式 + */ + private boolean tcp; + + /** + * 本地使用的端口 + */ + private int srcPort; + + /** + * 发送时,rtp的pt(uint8_t),不传时默认为96 + */ + private int pt; + + /** + * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; + */ + private boolean ps; + + /** + * 是否只有音频 + */ + private boolean onlyAudio; + + + public static RequestPushStreamMsg getInstance(String mediaServerId, String app, String stream, String ip, int port, String ssrc, + boolean tcp, int srcPort, int pt, boolean ps, boolean onlyAudio) { + RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg(); + requestPushStreamMsg.setMediaServerId(mediaServerId); + requestPushStreamMsg.setApp(app); + requestPushStreamMsg.setStream(stream); + requestPushStreamMsg.setIp(ip); + requestPushStreamMsg.setPort(port); + requestPushStreamMsg.setSsrc(ssrc); + requestPushStreamMsg.setTcp(tcp); + requestPushStreamMsg.setSrcPort(srcPort); + requestPushStreamMsg.setPt(pt); + requestPushStreamMsg.setPs(ps); + requestPushStreamMsg.setOnlyAudio(onlyAudio); + return requestPushStreamMsg; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public boolean isTcp() { + return tcp; + } + + public void setTcp(boolean tcp) { + this.tcp = tcp; + } + + public int getSrcPort() { + return srcPort; + } + + public void setSrcPort(int srcPort) { + this.srcPort = srcPort; + } + + public int getPt() { + return pt; + } + + public void setPt(int pt) { + this.pt = pt; + } + + public boolean isPs() { + return ps; + } + + public void setPs(boolean ps) { + this.ps = ps; + } + + public boolean isOnlyAudio() { + return onlyAudio; + } + + public void setOnlyAudio(boolean onlyAudio) { + this.onlyAudio = onlyAudio; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/RequestSendItemMsg.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/RequestSendItemMsg.java new file mode 100644 index 0000000..e3c75b4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/RequestSendItemMsg.java @@ -0,0 +1,188 @@ +package com.yfd.monitor.service.bean; + +/** + * redis消息:请求下级回复推送信息 +* + */ +public class RequestSendItemMsg { + + /** + * 下级服务ID + */ + private String serverId; + + /** + * 下级服务ID + */ + private String mediaServerId; + + /** + * 流ID + */ + private String app; + + /** + * 应用名 + */ + private String stream; + + /** + * 目标IP + */ + private String ip; + + /** + * 目标端口 + */ + private int port; + + /** + * ssrc + */ + private String ssrc; + + /** + * 平台国标编号 + */ + private String platformId; + + /** + * 平台名称 + */ + private String platformName; + + /** + * 通道ID + */ + private String channelId; + + + /** + * 是否使用TCP + */ + private Boolean isTcp; + + + /** + * 是否使用TCP + */ + private Boolean rtcp; + + + + + public static RequestSendItemMsg getInstance(String serverId, String mediaServerId, String app, String stream, String ip, int port, + String ssrc, String platformId, String channelId, Boolean isTcp, Boolean rtcp, String platformName) { + RequestSendItemMsg requestSendItemMsg = new RequestSendItemMsg(); + requestSendItemMsg.setServerId(serverId); + requestSendItemMsg.setMediaServerId(mediaServerId); + requestSendItemMsg.setApp(app); + requestSendItemMsg.setStream(stream); + requestSendItemMsg.setIp(ip); + requestSendItemMsg.setPort(port); + requestSendItemMsg.setSsrc(ssrc); + requestSendItemMsg.setPlatformId(platformId); + requestSendItemMsg.setPlatformName(platformName); + requestSendItemMsg.setChannelId(channelId); + requestSendItemMsg.setTcp(isTcp); + requestSendItemMsg.setRtcp(rtcp); + + return requestSendItemMsg; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getPlatformName() { + return platformName; + } + + public void setPlatformName(String platformName) { + this.platformName = platformName; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public Boolean getTcp() { + return isTcp; + } + + public void setTcp(Boolean tcp) { + isTcp = tcp; + } + + public Boolean getRtcp() { + return rtcp; + } + + public void setRtcp(Boolean rtcp) { + this.rtcp = rtcp; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ResponseSendItemMsg.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ResponseSendItemMsg.java new file mode 100644 index 0000000..bd7c4b6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ResponseSendItemMsg.java @@ -0,0 +1,30 @@ +package com.yfd.monitor.service.bean; + +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; + +/** + * redis消息:下级回复推送信息 + */ +public class ResponseSendItemMsg { + + private SendRtpItem sendRtpItem; + + private MediaServerItem mediaServerItem; + + public SendRtpItem getSendRtpItem() { + return sendRtpItem; + } + + public void setSendRtpItem(SendRtpItem sendRtpItem) { + this.sendRtpItem = sendRtpItem; + } + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/SSRCInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/SSRCInfo.java new file mode 100644 index 0000000..dd699ac --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/SSRCInfo.java @@ -0,0 +1,38 @@ +package com.yfd.monitor.service.bean; + +public class SSRCInfo { + + private int port; + private String ssrc; + private String Stream; + + public SSRCInfo(int port, String ssrc, String stream) { + this.port = port; + this.ssrc = ssrc; + Stream = stream; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public String getStream() { + return Stream; + } + + public void setStream(String stream) { + Stream = stream; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/StreamPushItemFromRedis.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/StreamPushItemFromRedis.java new file mode 100644 index 0000000..cc010ea --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/StreamPushItemFromRedis.java @@ -0,0 +1,34 @@ +package com.yfd.monitor.service.bean; + + +public class StreamPushItemFromRedis { + private String app; + private String stream; + private long timeStamp; + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public long getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(long timeStamp) { + this.timeStamp = timeStamp; + } +} + + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ThirdPartyGB.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ThirdPartyGB.java new file mode 100644 index 0000000..fb063c3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/ThirdPartyGB.java @@ -0,0 +1,23 @@ +package com.yfd.monitor.service.bean; + +public class ThirdPartyGB { + + private String name; + private String nationalStandardNo; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getNationalStandardNo() { + return nationalStandardNo; + } + + public void setNationalStandardNo(String nationalStandardNo) { + this.nationalStandardNo = nationalStandardNo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/WvpRedisMsg.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/WvpRedisMsg.java new file mode 100644 index 0000000..eca2815 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/WvpRedisMsg.java @@ -0,0 +1,114 @@ +package com.yfd.monitor.service.bean; + + +public class WvpRedisMsg { + + public static WvpRedisMsg getInstance(String fromId, String toId, String type, String cmd, String serial, String content){ + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setFromId(fromId); + wvpRedisMsg.setToId(toId); + wvpRedisMsg.setType(type); + wvpRedisMsg.setCmd(cmd); + wvpRedisMsg.setSerial(serial); + wvpRedisMsg.setContent(content); + return wvpRedisMsg; + } + + private String fromId; + + private String toId; + /** + * req 请求, res 回复 + */ + private String type; + private String cmd; + + /** + * 消息的ID + */ + private String serial; + private Object content; + + private final static String requestTag = "req"; + private final static String responseTag = "res"; + + public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) { + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setType(requestTag); + wvpRedisMsg.setFromId(fromId); + wvpRedisMsg.setToId(toId); + wvpRedisMsg.setCmd(cmd); + wvpRedisMsg.setSerial(serial); + wvpRedisMsg.setContent(content); + return wvpRedisMsg; + } + + public static WvpRedisMsg getResponseInstance() { + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setType(responseTag); + return wvpRedisMsg; + } + + public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) { + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setType(responseTag); + wvpRedisMsg.setFromId(fromId); + wvpRedisMsg.setToId(toId); + wvpRedisMsg.setCmd(cmd); + wvpRedisMsg.setSerial(serial); + wvpRedisMsg.setContent(content); + return wvpRedisMsg; + } + + public static boolean isRequest(WvpRedisMsg wvpRedisMsg) { + return requestTag.equals(wvpRedisMsg.getType()); + } + + public String getSerial() { + return serial; + } + + public void setSerial(String serial) { + this.serial = serial; + } + + public String getFromId() { + return fromId; + } + + public void setFromId(String fromId) { + this.fromId = fromId; + } + + public String getToId() { + return toId; + } + + public void setToId(String toId) { + this.toId = toId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCmd() { + return cmd; + } + + public void setCmd(String cmd) { + this.cmd = cmd; + } + + public Object getContent() { + return content; + } + + public void setContent(Object content) { + this.content = content; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/bean/WvpRedisMsgCmd.java b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/WvpRedisMsgCmd.java new file mode 100644 index 0000000..135e929 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/bean/WvpRedisMsgCmd.java @@ -0,0 +1,10 @@ +package com.yfd.monitor.service.bean; + + + +public class WvpRedisMsgCmd { + + public static final String GET_SEND_ITEM = "GetSendItem"; + public static final String REQUEST_PUSH_STREAM = "RequestPushStream"; + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceAlarmServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceAlarmServiceImpl.java new file mode 100644 index 0000000..644a168 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceAlarmServiceImpl.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import com.yfd.monitor.service.IDeviceAlarmService; +import com.yfd.monitor.storager.dao.DeviceAlarmMapper; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class DeviceAlarmServiceImpl implements IDeviceAlarmService { + + @Autowired + private DeviceAlarmMapper deviceAlarmMapper; + + @Override + public PageInfo getAllAlarm(int page, int count, String deviceId, String alarmPriority, String alarmMethod, String alarmType, String startTime, String endTime) { + PageHelper.startPage(page, count); + List all = deviceAlarmMapper.query(deviceId, alarmPriority, alarmMethod, alarmType, startTime, endTime); + return new PageInfo<>(all); + } + + @Override + public void add(DeviceAlarm deviceAlarm) { + deviceAlarmMapper.add(deviceAlarm); + } + + @Override + public int clearAlarmBeforeTime(Integer id, List deviceIdList, String time) { + return deviceAlarmMapper.clearAlarmBeforeTime(id, deviceIdList, time); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceChannelServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceChannelServiceImpl.java new file mode 100644 index 0000000..7ec80ac --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceChannelServiceImpl.java @@ -0,0 +1,253 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.utils.Coordtransform; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.DeviceMapper; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + + +@Service +public class DeviceChannelServiceImpl implements IDeviceChannelService { + + private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private DeviceChannelMapper channelMapper; + + @Autowired + private DeviceMapper deviceMapper; + + @Override + public DeviceChannel updateGps(DeviceChannel deviceChannel, Device device) { + if (deviceChannel.getLongitude()*deviceChannel.getLatitude() > 0) { + if (device == null) { + device = deviceMapper.getDeviceByDeviceId(deviceChannel.getDeviceId()); + } + + + + if ("WGS84".equals(device.getGeoCoordSys())) { + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + Double[] position = Coordtransform.WGS84ToGCJ02(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeGcj02(position[0]); + deviceChannel.setLatitudeGcj02(position[1]); + }else if ("GCJ02".equals(device.getGeoCoordSys())) { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + Double[] position = Coordtransform.GCJ02ToWGS84(deviceChannel.getLongitude(), deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(position[0]); + deviceChannel.setLatitudeWgs84(position[1]); + }else { + deviceChannel.setLongitudeGcj02(0.00); + deviceChannel.setLatitudeGcj02(0.00); + deviceChannel.setLongitudeWgs84(0.00); + deviceChannel.setLatitudeWgs84(0.00); + } + }else { + deviceChannel.setLongitudeGcj02(deviceChannel.getLongitude()); + deviceChannel.setLatitudeGcj02(deviceChannel.getLatitude()); + deviceChannel.setLongitudeWgs84(deviceChannel.getLongitude()); + deviceChannel.setLatitudeWgs84(deviceChannel.getLatitude()); + } + return deviceChannel; + } + + @Override + public void updateChannel(String deviceId, DeviceChannel channel) { + String channelId = channel.getChannelId(); + channel.setDeviceId(deviceId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); + if (streamInfo != null) { + channel.setStreamId(streamInfo.getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + DeviceChannel deviceChannel = channelMapper.queryChannel(deviceId, channelId); + channel = updateGps(channel, null); + if (deviceChannel == null) { + channel.setCreateTime(now); + channelMapper.add(channel); + }else { + channelMapper.update(channel); + } + channelMapper.updateChannelSubCount(deviceId,channel.getParentId()); + } + + @Override + public int updateChannels(String deviceId, List channels) { + List addChannels = new ArrayList<>(); + List updateChannels = new ArrayList<>(); + HashMap channelsInStore = new HashMap<>(); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (channels != null && channels.size() > 0) { + List channelList = channelMapper.queryChannels(deviceId, null, null, null, null,null); + if (channelList.size() == 0) { + for (DeviceChannel channel : channels) { + channel.setDeviceId(deviceId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); + if (streamInfo != null) { + channel.setStreamId(streamInfo.getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel.setCreateTime(now); + channel = updateGps(channel, device); + addChannels.add(channel); + } + }else { + for (DeviceChannel deviceChannel : channelList) { + channelsInStore.put(deviceChannel.getChannelId(), deviceChannel); + } + for (DeviceChannel channel : channels) { + channel.setDeviceId(deviceId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); + if (streamInfo != null) { + channel.setStreamId(streamInfo.getStream()); + } + String now = DateUtil.getNow(); + channel.setUpdateTime(now); + channel = updateGps(channel, device); + if (channelsInStore.get(channel.getChannelId()) != null) { + updateChannels.add(channel); + }else { + addChannels.add(channel); + channel.setCreateTime(now); + } + } + } + int limitCount = 300; + if (addChannels.size() > 0) { + if (addChannels.size() > limitCount) { + for (int i = 0; i < addChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > addChannels.size()) { + toIndex = addChannels.size(); + } + channelMapper.batchAdd(addChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchAdd(addChannels); + } + } + if (updateChannels.size() > 0) { + if (updateChannels.size() > limitCount) { + for (int i = 0; i < updateChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > updateChannels.size()) { + toIndex = updateChannels.size(); + } + channelMapper.batchUpdate(updateChannels.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdate(updateChannels); + } + } + } + return addChannels.size() + updateChannels.size(); + } + + @Override + public ResourceBaceInfo getOverview() { + return channelMapper.getOverview(); + } + + + @Override + public List queryAllChannelList(String platformId) { + return channelMapper.queryChannelListInAll(null, null, null, platformId, null); + } + + @Override + public boolean updateAllGps(Device device) { + List deviceChannels = channelMapper.getChannelsWithoutTransform(device.getDeviceId()); + List result = new CopyOnWriteArrayList<>(); + if (deviceChannels.size() == 0) { + return true; + } + String now = DateUtil.getNow(); + deviceChannels.parallelStream().forEach(deviceChannel -> { + deviceChannel.setUpdateTime(now); + result.add(updateGps(deviceChannel, device)); + }); + int limitCount = 300; + if (result.size() > limitCount) { + for (int i = 0; i < result.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > result.size()) { + toIndex = result.size(); + } + channelMapper.batchUpdate(result.subList(i, toIndex)); + } + }else { + channelMapper.batchUpdate(result); + } + + return true; + } + + @Override + public List getDeviceByChannelId(String channelId) { + + return channelMapper.getDeviceByChannelId(channelId); + } + + @Override + public int deleteChannels(List deleteChannelList) { + return channelMapper.batchDel(deleteChannelList); + } + + @Override + public int channelsOnline(List channels) { + return channelMapper.batchOnline(channels); + } + + @Override + public int channelsOffline(List channels) { + return channelMapper.batchOffline(channels); + } + + @Override + public DeviceChannel getOne(String deviceId, String channelId){ + return channelMapper.queryChannel(deviceId, channelId); + } + + @Override + public void batchUpdateChannel(List channels) { + channelMapper.batchUpdate(channels); + for (DeviceChannel channel : channels) { + if (channel.getParentId() != null) { + channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId()); + } + } + } + + @Override + public void batchAddChannel(List channels) { + channelMapper.batchAdd(channels); + for (DeviceChannel channel : channels) { + if (channel.getParentId() != null) { + channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId()); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceServiceImpl.java new file mode 100644 index 0000000..33efa7e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/DeviceServiceImpl.java @@ -0,0 +1,731 @@ +package com.yfd.monitor.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.WebSocketServer; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.task.ISubscribeTask; +import com.yfd.monitor.gdw2019.task.impl.CatalogSubscribeTask; +import com.yfd.monitor.gdw2019.task.impl.MobilePositionSubscribeTask; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import com.yfd.monitor.gdw2019.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.DeviceMapper; +import com.yfd.monitor.storager.dao.PlatformChannelMapper; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.BaseTree; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import javax.annotation.Resource; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 设备业务(目录订阅) + */ +@Service +public class DeviceServiceImpl implements IDeviceService { + + private final static Logger logger = LoggerFactory.getLogger(DeviceServiceImpl.class); + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ISIPCommander sipCommander; + + @Autowired + private CatalogResponseMessageHandler catalogResponseMessageHandler; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Resource + DataSourceTransactionManager dataSourceTransactionManager; + + @Resource + TransactionDefinition transactionDefinition; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ISIPCommander commander; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private IMediaServerService mediaServerService; + + @Value("${user-settings.station.id}") + private String stationid; + + @Autowired + private EventPublisher publisher; + + @Override + public void online(Device device, SipTransactionInfo sipTransactionInfo) { + logger.info("[接收到设备心跳] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); + Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId()); + Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); + + String now = DateUtil.getNow(); + if (deviceInRedis != null && deviceInDb == null) { + // redis 存在脏数据 + redisCatchStorage.clearCatchByDeviceId(device.getDeviceId()); + } + device.setUpdateTime(now); + if (device.getKeepaliveIntervalTime() == 0) { + // 默认心跳间隔180 + device.setKeepaliveIntervalTime(60); + } + if (sipTransactionInfo != null) { + device.setSipTransactionInfo(sipTransactionInfo); + }else { + if (deviceInRedis != null) { + device.setSipTransactionInfo(deviceInRedis.getSipTransactionInfo()); + } + } + + // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询 + if (device.getCreateTime() == null) { + device.setOnline(1); + device.setCreateTime(now); + logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + try { + //增加插入设备表数据 + device.setId(IdUtil.fastSimpleUUID()); + device.setStationId(stationid); + deviceMapper.add(device); + if(StrUtil.isNotEmpty(device.getModel())&&device.getModel().equals("StationNode")){ + deviceMapper.updateRiisSubstation(device);// 更新边缘节点状态 +// WebSocketServer.sendInfo("substation_online", "1"); + + }else{ + int count=deviceMapper.getRiisPatrolDevice(device.getDeviceId()); + if(count==0){ + deviceMapper.addRiisPatrolDevice(device); + } + else{ + deviceMapper.updateRiisPatrolDevice(device); + } + } + + redisCatchStorage.updateDevice(device); + result = true; + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }catch (Exception e) { + dataSourceTransactionManager.rollback(transactionStatus); + } + + + try { + commander.deviceInfoQuery(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); + } + sync(device); + }else { + if(device.getOnline() == 0){ + device.setOnline(1); + device.setCreateTime(now); + deviceMapper.update(device); + if(StrUtil.isNotEmpty(device.getModel())&&device.getModel().equals("StationNode")){ + deviceMapper.updateRiisSubstation(device);// 更新边缘节点状态 + WebSocketServer.sendInfo("substation_online", "1"); + }else{ + deviceMapper.updateRiisPatrolDevice(device); + } + redisCatchStorage.updateDevice(device); + if (userSetting.getSyncChannelOnDeviceOnline()) { + logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + try { + commander.deviceInfoQuery(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); + } + sync(device); + // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台 + } + }else { + if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) { + logger.info("[查询通道信息]: {},通道数为0,查询通道信息", device.getDeviceId()); + sync(device); + } + deviceMapper.update(device); + if(StrUtil.isNotEmpty(device.getModel())&&device.getModel().equals("StationNode")){ + deviceMapper.updateRiisSubstation(device);// 更新边缘节点状态 + WebSocketServer.sendInfo("substation_online", "1"); + }else{ + deviceMapper.updateRiisPatrolDevice(device); + } + redisCatchStorage.updateDevice(device); + } + + } + + // 上线添加订阅 + if (device.getSubscribeCycleForCatalog() > 0) { + // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 + addCatalogSubscribe(device); + } + if (device.getSubscribeCycleForMobilePosition() > 0) { + addMobilePositionSubscribe(device); + } + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线 + dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3); + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, true); + } + // 添加设备订阅信息 + Map deviceStatus=new HashMap<>(); + deviceStatus.put("code",device.getDeviceId()); + deviceStatus.put("Name","设备上线"); + deviceStatus.put("Status","1"); + publisher.deviceStatusEventPublish(deviceStatus); //发布设备上线通知 + } + + @Override + public void offline(String deviceId, String reason) { + logger.error("[设备离线],{}, device:{}", reason, deviceId); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return; + } + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId; + dynamicTask.stop(registerExpireTaskKey); + device.setOnline(0); + redisCatchStorage.updateDevice(device); + deviceMapper.update(device); + if(StrUtil.isNotEmpty(device.getModel())&&device.getModel().equals("StationNode")){ + deviceMapper.updateRiisSubstation(device);// 更新变电站边缘节点状态 + WebSocketServer.sendInfo("substation_online", "0"); + }else{ + deviceMapper.updateRiisPatrolDevice(device); + } + //进行通道离线 + //deviceChannelMapper.offlineByDeviceId(deviceId); + // 离线释放所有ssrc + List ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); + if (ssrcTransactions != null && ssrcTransactions.size() > 0) { + for (SsrcTransaction ssrcTransaction : ssrcTransactions) { + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); + streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + } + } + // 移除订阅 + removeCatalogSubscribe(device); + removeMobilePositionSubscribe(device); + if (userSetting.getDeviceStatusNotify()) { + // 发送redis消息 + redisCatchStorage.sendDeviceOrChannelStatus(device.getDeviceId(), null, false); + } + + // 添加设备订阅信息 + Map deviceStatus=new HashMap<>(); + deviceStatus.put("code",device.getDeviceId()); + deviceStatus.put("Name","设备离线"); + deviceStatus.put("Status","0"); + publisher.deviceStatusEventPublish(deviceStatus); //发布设备离线通知 + } + + @Override + public boolean addCatalogSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForCatalog() < 0) { + return false; + } + logger.info("[添加目录订阅] 设备{}", device.getDeviceId()); + // 添加目录订阅 + CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask); + // 刷新订阅 + int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30); + // 设置最小值为30 + dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000); + return true; + } + + @Override + public boolean removeCatalogSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForCatalog() < 0) { + return false; + } + logger.info("[移除目录订阅]: {}", device.getDeviceId()); + String taskKey = device.getDeviceId() + "catalog"; + if (device.getOnline() == 1) { + Runnable runnable = dynamicTask.get(taskKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + } + dynamicTask.stop(taskKey); + return true; + } + + @Override + public boolean addMobilePositionSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForMobilePosition() < 0) { + return false; + } + logger.info("[添加移动位置订阅] 设备{}", device.getDeviceId()); + // 添加目录订阅 + MobilePositionSubscribeTask mobilePositionSubscribeTask = new MobilePositionSubscribeTask(device, sipCommander, dynamicTask); + // 设置最小值为30 + int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30); + // 刷新订阅 + dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog) * 1000); + return true; + } + + @Override + public boolean removeMobilePositionSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForCatalog() < 0) { + return false; + } + logger.info("[移除移动位置订阅]: {}", device.getDeviceId()); + String taskKey = device.getDeviceId() + "mobile_position"; + if (device.getOnline() == 1) { + Runnable runnable = dynamicTask.get(taskKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + } + dynamicTask.stop(taskKey); + return true; + } + + @Override + public SyncStatus getChannelSyncStatus(String deviceId) { + return catalogResponseMessageHandler.getChannelSyncProgress(deviceId); + } + + @Override + public Boolean isSyncRunning(String deviceId) { + return catalogResponseMessageHandler.isSyncRunning(deviceId); + } + + @Override + public void sync(Device device) { + if (catalogResponseMessageHandler.isSyncRunning(device.getDeviceId())) { + logger.info("开启同步时发现同步已经存在"); + return; + } + int sn = (int)((Math.random()*9+1)*100000); + catalogResponseMessageHandler.setChannelSyncReady(device, sn); + try { + sipCommander.catalogQuery(device, sn, event -> { + String errorMsg = String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg); + catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); + }); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[同步通道], 信令发送失败:{}", e.getMessage() ); + String errorMsg = String.format("同步通道失败,信令发送失败: %s", e.getMessage()); + catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); + } + } + + @Override + public Device getDevice(String deviceId) { + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null) { + device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device != null) { + redisCatchStorage.updateDevice(device); + } + } + return device; + } + + @Override + public List getAllOnlineDevice() { + return deviceMapper.getOnlineDevices(); + } + + @Override + public boolean expire(Device device) { + Instant registerTimeDate = Instant.from(DateUtil.formatter.parse(device.getRegisterTime())); + Instant expireInstant = registerTimeDate.plusMillis(TimeUnit.SECONDS.toMillis(device.getExpires())); + return expireInstant.isBefore(Instant.now()); + } + + @Override + public void checkDeviceStatus(Device device) { + if (device == null || device.getOnline() == 0) { + return; + } + try { + sipCommander.deviceStatusQuery(device, null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 设备状态查询: {}", e.getMessage()); + } + + } + + @Override + public Device getDeviceByHostAndPort(String host, int port) { + return deviceMapper.getDeviceByHostAndPort(host, port); + } + + @Override + public synchronized void updateDevice(Device device) { + device.setCharset(device.getCharset().toUpperCase()); + device.setUpdateTime(DateUtil.getNow()); + if (deviceMapper.update(device) > 0) { + redisCatchStorage.updateDevice(device); + } + +// String now = DateUtil.getNow(); +// device.setUpdateTime(now); +// device.setCharset(device.getCharset().toUpperCase()); +// device.setUpdateTime(DateUtil.getNow()); +// Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); +// if (deviceByDeviceId.getOnline() != 1) { +// deviceMapper.update(device); +// } +// redisCatchStorage.updateDevice(device); + } + + /** + * 更新通道坐标系 + */ + private void updateDeviceChannelGeoCoordSys(Device device) { + List deviceChannels = deviceChannelMapper.getAllChannelWithCoordinate(device.getDeviceId()); + if (deviceChannels.size() > 0) { + List deviceChannelsForStore = new ArrayList<>(); + for (DeviceChannel deviceChannel : deviceChannels) { + deviceChannelsForStore.add(deviceChannelService.updateGps(deviceChannel, device)); + } + deviceChannelService.updateChannels(device.getDeviceId(), deviceChannelsForStore); + } + } + + + @Override + public List> queryVideoDeviceTree(String deviceId, String parentId, boolean onlyCatalog) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return null; + } + if (parentId == null || parentId.equals(deviceId)) { + // 字根节点开始查询 + List rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), true, !onlyCatalog); + return transportChannelsToTree(rootNodes, ""); + } + + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) { + if (parentId.length()%2 != 0) { + return null; + } + // 使用行政区划展示树 +// if (parentId.length() > 10) { +// // TODO 可能是行政区划与业务分组混杂的情形 +// return null; +// } + + if (parentId.length() == 10 ) { + if (onlyCatalog) { + return null; + } + // parentId为行业编码, 其下不会再有行政区划 + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + List> trees = transportChannelsToTree(channels, parentId); + return trees; + } + // 查询其下的行政区划和摄像机 + List channelsForCivilCode = deviceChannelMapper.getChannelsWithCivilCodeAndLength(deviceId, parentId, parentId.length() + 2); + if (!onlyCatalog) { + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + + for(DeviceChannel channel : channels) { + boolean flag = false; + for(DeviceChannel deviceChannel : channelsForCivilCode) { + if(channel.getChannelId().equals(deviceChannel.getChannelId())) { + flag = true; + } + } + if(!flag) { + channelsForCivilCode.add(channel); + } + } + } + List> trees = transportChannelsToTree(channelsForCivilCode, parentId); + return trees; + + } + // 使用业务分组展示树 + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) { + if (parentId.length() < 14 ) { + return null; + } + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, parentId, null, null, null,null); + List> trees = transportChannelsToTree(deviceChannels, parentId); + return trees; + } + + return null; + } + + @Override + public List queryVideoDeviceInTreeNode(String deviceId, String parentId) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return null; + } + if (parentId == null || parentId.equals(deviceId)) { + // 字根节点开始查询 + List rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), false, true); + return rootNodes; + } + + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) { + if (parentId.length()%2 != 0) { + return null; + } + // 使用行政区划展示树 + if (parentId.length() > 10) { + // TODO 可能是行政区划与业务分组混杂的情形 + return null; + } + + if (parentId.length() == 10 ) { + // parentId为行业编码, 其下不会再有行政区划 + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + return channels; + } + // 查询其下的行政区划和摄像机 + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + return channels; + + } + // 使用业务分组展示树 + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) { + if (parentId.length() < 14 ) { + return null; + } + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, parentId, null, null, null,null); + return deviceChannels; + } + + return null; + } + + private List> transportChannelsToTree(List channels, String parentId) { + if (channels == null) { + return null; + } + List> treeNotes = new ArrayList<>(); + if (channels.size() == 0) { + return treeNotes; + } + for (DeviceChannel channel : channels) { + + BaseTree node = new BaseTree<>(); + node.setId(channel.getChannelId()); + node.setDeviceId(channel.getDeviceId()); + node.setName(channel.getName()); + node.setPid(parentId); + node.setBasicData(channel); + node.setParent(false); + if (channel.getChannelId().length() > 8) { + String gbCodeType = channel.getChannelId().substring(10, 13); + node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) ); + }else { + node.setParent(true); + } + treeNotes.add(node); + } + Collections.sort(treeNotes); + return treeNotes; + } + + private List getRootNodes(String deviceId, boolean isCivilCode, boolean haveCatalog, boolean haveChannel) { + if (!haveCatalog && !haveChannel) { + return null; + } + List result = new ArrayList<>(); + if (isCivilCode) { + // 使用行政区划 + Integer length= deviceChannelMapper.getChannelMinLength(deviceId); + if (length == null) { + return null; + } + if (length <= 10) { + if (haveCatalog) { + List provinceNode = deviceChannelMapper.getChannelsWithCivilCodeAndLength(deviceId, null, length); + if (provinceNode != null && provinceNode.size() > 0) { + result.addAll(provinceNode); + } + } + + if (haveChannel) { + // 查询那些civilCode不在通道中的不规范通道,放置在根目录 + List nonstandardNode = deviceChannelMapper.getChannelWithoutCiviCode(deviceId); + if (nonstandardNode != null && nonstandardNode.size() > 0) { + result.addAll(nonstandardNode); + } + } + }else { + if (haveChannel) { + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, null, null, null, null,null); + if (deviceChannels != null && deviceChannels.size() > 0) { + result.addAll(deviceChannels); + } + } + } + + }else { + // 使用业务分组+虚拟组织 + + // 只获取业务分组 + List deviceChannels = deviceChannelMapper.getBusinessGroups(deviceId, ChannelIdType.BUSINESS_GROUP); + if (deviceChannels != null && deviceChannels.size() > 0) { + result.addAll(deviceChannels); + } + } + return result; + } + + @Override + public boolean isExist(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId) != null; + } + + @Override + public void addDevice(Device device) { + device.setOnline(0); + device.setCreateTime(DateUtil.getNow()); + device.setUpdateTime(DateUtil.getNow()); + deviceMapper.addCustomDevice(device); + } + + @Override + public void updateCustomDevice(Device device) { + Device deviceInStore = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); + if (deviceInStore == null) { + logger.warn("更新设备时未找到设备信息"); + return; + } + if (!ObjectUtils.isEmpty(device.getName())) { + deviceInStore.setName(device.getName()); + } + if (!ObjectUtils.isEmpty(device.getCharset())) { + deviceInStore.setCharset(device.getCharset()); + } + if (!ObjectUtils.isEmpty(device.getMediaServerId())) { + deviceInStore.setMediaServerId(device.getMediaServerId()); + } + deviceInStore.setSdpIp(device.getSdpIp()); + deviceInStore.setCharset(device.getCharset()); + deviceInStore.setTreeType(device.getTreeType()); + + // 目录订阅相关的信息 + if (device.getSubscribeCycleForCatalog() > 0) { + if (deviceInStore.getSubscribeCycleForCatalog() == 0 || deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { + deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); + // 开启订阅 + addCatalogSubscribe(deviceInStore); + } + }else if (device.getSubscribeCycleForCatalog() == 0) { + if (deviceInStore.getSubscribeCycleForCatalog() != 0) { + deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); + // 取消订阅 + removeCatalogSubscribe(deviceInStore); + } + } + + // 移动位置订阅相关的信息 + if (device.getSubscribeCycleForMobilePosition() > 0) { + if (deviceInStore.getSubscribeCycleForMobilePosition() == 0 || deviceInStore.getSubscribeCycleForMobilePosition() != device.getSubscribeCycleForMobilePosition()) { + deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval()); + deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition()); + // 开启订阅 + addMobilePositionSubscribe(deviceInStore); + } + }else if (device.getSubscribeCycleForMobilePosition() == 0) { + if (deviceInStore.getSubscribeCycleForMobilePosition() != 0) { + // 取消订阅 + removeMobilePositionSubscribe(deviceInStore); + } + } + // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标 + if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) { + updateDeviceChannelGeoCoordSys(device); + } + // 更新redis + redisCatchStorage.updateDevice(device); + deviceMapper.updateCustom(device); + } + + @Override + public boolean delete(String deviceId) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + try { + platformChannelMapper.delChannelForDeviceId(deviceId); + deviceChannelMapper.cleanChannelsByDeviceId(deviceId); + if ( deviceMapper.del(deviceId) < 0 ) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + } + result = true; + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }catch (Exception e) { + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public ResourceBaceInfo getOverview() { + return deviceMapper.getOverview(); + } + + @Override + public List getAll() { + return deviceMapper.getAll(); + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/GbStreamServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/GbStreamServiceImpl.java new file mode 100644 index 0000000..a1916d3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/GbStreamServiceImpl.java @@ -0,0 +1,268 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.storager.dao.GbStreamMapper; +import com.yfd.monitor.storager.dao.ParentPlatformMapper; +import com.yfd.monitor.storager.dao.PlatformCatalogMapper; +import com.yfd.monitor.storager.dao.PlatformGbStreamMapper; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class GbStreamServiceImpl implements IGbStreamService { + + private final static Logger logger = LoggerFactory.getLogger(GbStreamServiceImpl.class); + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private PlatformCatalogMapper catalogMapper; + + @Autowired + private EventPublisher eventPublisher; + + @Override + public PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId, String query, String mediaServerId) { + PageHelper.startPage(page, count); + List all = gbStreamMapper.selectAll(platFormId, catalogId, query, mediaServerId); + return new PageInfo<>(all); + } + + @Override + public void del(String app, String stream) { + gbStreamMapper.del(app, stream); + } + + + @Override + public boolean addPlatformInfo(List gbStreams, String platformId, String catalogId) { + // 放在事务内执行 + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + ParentPlatform parentPlatform = platformMapper.getParentPlatByServerGBId(platformId); + if (catalogId == null) { + catalogId = parentPlatform.getCatalogId(); + } + try { + List deviceChannelList = new ArrayList<>(); + for (int i = 0; i < gbStreams.size(); i++) { + GbStream gbStream = gbStreams.get(i); + gbStream.setCatalogId(catalogId); + gbStream.setPlatformId(platformId); + // TODO 修改为批量提交 + platformGbStreamMapper.add(gbStream); + logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1); + DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform); + deviceChannelList.add(deviceChannelListByStream); + } + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + if (subscribeHolder.getCatalogSubscribe(platformId) != null) { + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); + } + + result = true; + }catch (Exception e) { + logger.error("批量保存流与平台的关系时错误", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public DeviceChannel getDeviceChannelListByStream(GbStream gbStream, String catalogId, ParentPlatform platform) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannel.setName(gbStream.getName()); + deviceChannel.setLongitude(gbStream.getLongitude()); + deviceChannel.setLatitude(gbStream.getLatitude()); + deviceChannel.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("MonitorServer"); + deviceChannel.setStatus(gbStream.isStatus()?1:0); + + deviceChannel.setRegisterWay(1); + + PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platform.getServerGBId(), catalogId); + if (catalog != null) { + deviceChannel.setCivilCode(catalog.getCivilCode()); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + }else { + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + deviceChannel.setParentId(platform.getDeviceGBId()); + } + + deviceChannel.setModel("live"); + deviceChannel.setOwner("MonitorServer"); + deviceChannel.setParental(0); + deviceChannel.setSecrecy("0"); + return deviceChannel; + } + + @Override + public boolean delPlatformInfo(String platformId, List gbStreams) { + // 放在事务内执行 + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + try { + List deviceChannelList = new ArrayList<>(); + platformGbStreamMapper.delByAppAndStreamsByPlatformId(gbStreams, platformId); + for (GbStream gbStream : gbStreams) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannelList.add(deviceChannel); + } + + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + result = true; + }catch (Exception e) { + logger.error("批量移除流与平台的关系时错误", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public void sendCatalogMsg(GbStream gbStream, String type) { + if (gbStream == null || type == null) { + logger.warn("[发送目录订阅]类型:流信息或类型为NULL"); + return; + } + List gbStreams = new ArrayList<>(); + if (gbStream.getGbId() != null) { + gbStreams.add(gbStream); + }else { + GbStream gbStreamIndb = gbStreamMapper.selectOne(gbStream.getApp(), gbStream.getStream()); + if (gbStreamIndb != null && gbStreamIndb.getGbId() != null){ + gbStreams.add(gbStreamIndb); + } + } + sendCatalogMsgs(gbStreams, type); + } + + @Override + public void sendCatalogMsgs(List gbStreams, String type) { + if (gbStreams.size() > 0) { + for (GbStream gs : gbStreams) { + if (ObjectUtils.isEmpty(gs.getGbId())){ + continue; + } + List parentPlatforms = platformGbStreamMapper.selectByAppAndStream(gs.getApp(), gs.getStream()); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + if (parentPlatform != null) { + eventPublisher.catalogEventPublishForStream(parentPlatform.getServerGBId(), gs, type); + } + } + } + } + } + } + + @Override + public int updateGbIdOrName(List streamPushItemForUpdate) { + return gbStreamMapper.updateGbIdOrName(streamPushItemForUpdate); + } + + @Override + public DeviceChannel getDeviceChannelListByStreamWithStatus(GbStream gbStream, String catalogId, ParentPlatform platform) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannel.setName(gbStream.getName()); + deviceChannel.setLongitude(gbStream.getLongitude()); + deviceChannel.setLatitude(gbStream.getLatitude()); + deviceChannel.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("MonitorServer"); + // todo 目前是每一条查询一次,需要优化 + Boolean status = null; + if ("proxy".equals(gbStream.getStreamType())) { + status = gbStreamMapper.selectStatusForProxy(gbStream.getApp(), gbStream.getStream()); + }else { + status = gbStreamMapper.selectStatusForPush(gbStream.getApp(), gbStream.getStream()); + } + deviceChannel.setStatus((status != null && status)?1:0); + + deviceChannel.setRegisterWay(1); + PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platform.getServerGBId(), catalogId); + if (catalog != null) { + deviceChannel.setCivilCode(catalog.getCivilCode()); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + }else { + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + deviceChannel.setParentId(platform.getDeviceGBId()); + } + + deviceChannel.setModel("live"); + deviceChannel.setOwner("MonitorServer"); + deviceChannel.setParental(0); + deviceChannel.setSecrecy("0"); + return deviceChannel; + } + + @Override + public List getAllGBChannels(String platformId) { + + return gbStreamMapper.selectAll(platformId, null, null, null); + + } + + @Override + public void delAllPlatformInfo(String platformId, String catalogId) { + if (platformId == null) { + return ; + } + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + return ; + } + if (ObjectUtils.isEmpty(catalogId)) { + catalogId = null; + } + List gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId); + List deviceChannelList = new ArrayList<>(); + for (GbStream gbStream : gbStreams) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannelList.add(deviceChannel); + } + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId); + } + + @Override + public List getGbChannelWithGbid(String gbId) { + return gbStreamMapper.selectByGBId(gbId); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/InviteStreamServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/InviteStreamServiceImpl.java new file mode 100644 index 0000000..0ae0c83 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/InviteStreamServiceImpl.java @@ -0,0 +1,271 @@ +package com.yfd.monitor.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.yfd.monitor.common.InviteInfo; +import com.yfd.monitor.common.InviteSessionStatus; +import com.yfd.monitor.common.InviteSessionType; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.service.IInviteStreamService; +import com.yfd.monitor.service.bean.ErrorCallback; +import com.yfd.monitor.utils.redis.RedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +@Service + +public class InviteStreamServiceImpl implements IInviteStreamService { + + private final Logger logger = LoggerFactory.getLogger(InviteStreamServiceImpl.class); + + private final Map>> inviteErrorCallbackMap = new ConcurrentHashMap<>(); + + @Autowired + private RedisTemplate redisTemplate; + + @Override + public void updateInviteInfo(InviteInfo inviteInfo) { + if (inviteInfo == null || (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null)) { + logger.warn("[更新Invite信息],参数不全: {}", JSON.toJSON(inviteInfo)); + return; + } + InviteInfo inviteInfoForUpdate = null; + + if (InviteSessionStatus.ready == inviteInfo.getStatus()) { + if (inviteInfo.getDeviceId() == null + || inviteInfo.getChannelId() == null + || inviteInfo.getType() == null + || inviteInfo.getStream() == null + ) { + return; + } + inviteInfoForUpdate = inviteInfo; + } else { + InviteInfo inviteInfoInRedis = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), + inviteInfo.getChannelId(), inviteInfo.getStream()); + if (inviteInfoInRedis == null) { + logger.warn("[更新Invite信息],未从缓存中读取到Invite信息: deviceId: {}, channel: {}, stream: {}", + inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + return; + } + if (inviteInfo.getStreamInfo() != null) { + inviteInfoInRedis.setStreamInfo(inviteInfo.getStreamInfo()); + } + if (inviteInfo.getSsrcInfo() != null) { + inviteInfoInRedis.setSsrcInfo(inviteInfo.getSsrcInfo()); + } + if (inviteInfo.getStreamMode() != null) { + inviteInfoInRedis.setStreamMode(inviteInfo.getStreamMode()); + } + if (inviteInfo.getReceiveIp() != null) { + inviteInfoInRedis.setReceiveIp(inviteInfo.getReceiveIp()); + } + if (inviteInfo.getReceivePort() != null) { + inviteInfoInRedis.setReceivePort(inviteInfo.getReceivePort()); + } + if (inviteInfo.getStatus() != null) { + inviteInfoInRedis.setStatus(inviteInfo.getStatus()); + } + + inviteInfoForUpdate = inviteInfoInRedis; + + } + String key = VideoManagerConstants.INVITE_PREFIX + + ":" + inviteInfoForUpdate.getType() + + ":" + inviteInfoForUpdate.getDeviceId() + + ":" + inviteInfoForUpdate.getChannelId() + + ":" + inviteInfoForUpdate.getStream()+ + ":" + inviteInfoForUpdate.getSsrcInfo().getSsrc(); + redisTemplate.opsForValue().set(key, inviteInfoForUpdate); + } + + @Override + public InviteInfo updateInviteInfoForStream(InviteInfo inviteInfo, String stream) { + + InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + if (inviteInfoInDb == null) { + return null; + } + removeInviteInfo(inviteInfoInDb); + String key = VideoManagerConstants.INVITE_PREFIX + + ":" + inviteInfo.getType() + + ":" + inviteInfo.getDeviceId() + + ":" + inviteInfo.getChannelId() + + ":" + stream + + ":" + inviteInfo.getSsrcInfo().getSsrc(); + inviteInfoInDb.setStream(stream); + if (inviteInfoInDb.getSsrcInfo() != null) { + inviteInfoInDb.getSsrcInfo().setStream(stream); + } + redisTemplate.opsForValue().set(key, inviteInfoInDb); + return inviteInfoInDb; + } + + @Override + public InviteInfo getInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = VideoManagerConstants.INVITE_PREFIX + + ":" + (type != null ? type : "*") + + ":" + (deviceId != null ? deviceId : "*") + + ":" + (channelId != null ? channelId : "*") + + ":" + (stream != null ? stream : "*") + + ":*"; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.isEmpty()) { + return null; + } + if (scanResult.size() != 1) { + logger.warn("[获取InviteInfo] 发现 key: {}存在多条", key); + } + + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); + } + + @Override + public InviteInfo getInviteInfoByDeviceAndChannel(InviteSessionType type, String deviceId, String channelId) { + return getInviteInfo(type, deviceId, channelId, null); + } + + @Override + public InviteInfo getInviteInfoByStream(InviteSessionType type, String stream) { + return getInviteInfo(type, null, null, stream); + } + + @Override + public void removeInviteInfo(InviteSessionType type, String deviceId, String channelId, String stream) { + String scanKey = VideoManagerConstants.INVITE_PREFIX + + ":" + (type != null ? type : "*") + + ":" + (deviceId != null ? deviceId : "*") + + ":" + (channelId != null ? channelId : "*") + + ":" + (stream != null ? stream : "*") + + ":*"; + List scanResult = RedisUtil.scan(redisTemplate, scanKey); + if (scanResult.size() > 0) { + for (Object keyObj : scanResult) { + String key = (String) keyObj; + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); + if (inviteInfo == null) { + continue; + } + redisTemplate.delete(key); + inviteErrorCallbackMap.remove(buildKey(type, deviceId, channelId, inviteInfo.getStream())); + } + } + } + + @Override + public void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, String deviceId, String channelId) { + removeInviteInfo(inviteSessionType, deviceId, channelId, null); + } + + @Override + public void removeInviteInfo(InviteInfo inviteInfo) { + removeInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + } + + @Override + public void once(InviteSessionType type, String deviceId, String channelId, String stream, ErrorCallback callback) { + String key = buildKey(type, deviceId, channelId, stream); + List> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null) { + callbacks = new CopyOnWriteArrayList<>(); + inviteErrorCallbackMap.put(key, callbacks); + } + callbacks.add(callback); + + } + + private String buildKey(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = type + ":" + deviceId + ":" + channelId; + // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite + if (stream != null) { + key += (":" + stream); + } + return key; + } + + + @Override + public void clearInviteInfo(String deviceId) { + removeInviteInfo(null, deviceId, null, null); + } + + @Override + public int getStreamInfoCount(String mediaServerId) { + int count = 0; + String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:*"; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() == 0) { + return 0; + }else { + for (Object keyObj : scanResult) { + String keyStr = (String) keyObj; + InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(keyStr); + if (inviteInfo != null && inviteInfo.getStreamInfo() != null && inviteInfo.getStreamInfo().getMediaServerId().equals(mediaServerId)) { + count++; + } + } + } + return count; + } + + @Override + public void call(InviteSessionType type, String deviceId, String channelId, String stream, int code, String msg, Object data) { + String key = buildSubStreamKey(type, deviceId, channelId, stream); + List> callbacks = inviteErrorCallbackMap.get(key); + if (callbacks == null) { + return; + } + for (ErrorCallback callback : callbacks) { + callback.run(code, msg, data); + } + inviteErrorCallbackMap.remove(key); + } + + + private String buildSubStreamKey(InviteSessionType type, String deviceId, String channelId, String stream) { + String key = type + ":" + ":" + deviceId + ":" + channelId; + // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite + if (stream != null) { + key += (":" + stream); + } + return key; + } + + @Override + public InviteInfo getInviteInfoBySSRC(String ssrc) { + String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:" + ssrc; + List scanResult = RedisUtil.scan(redisTemplate, key); + if (scanResult.size() != 1) { + return null; + } + + return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); + } + + @Override + public InviteInfo updateInviteInfoForSSRC(InviteInfo inviteInfo, String ssrc) { + InviteInfo inviteInfoInDb = getInviteInfo(inviteInfo.getType(), inviteInfo.getDeviceId(), inviteInfo.getChannelId(), inviteInfo.getStream()); + if (inviteInfoInDb == null) { + return null; + } + removeInviteInfo(inviteInfoInDb); + String key = VideoManagerConstants.INVITE_PREFIX + + ":" + inviteInfo.getType() + + ":" + inviteInfo.getDeviceId() + + ":" + inviteInfo.getChannelId() + + ":" + inviteInfo.getStream() + + ":" + ssrc; + if (inviteInfoInDb.getSsrcInfo() != null) { + inviteInfoInDb.getSsrcInfo().setSsrc(ssrc); + } + redisTemplate.opsForValue().set(key, inviteInfoInDb); + return inviteInfoInDb; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/LogServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/LogServiceImpl.java new file mode 100644 index 0000000..79250ca --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/LogServiceImpl.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.service.ILogService; +import com.yfd.monitor.storager.dao.LogMapper; +import com.yfd.monitor.storager.dao.dto.LogDto; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class LogServiceImpl implements ILogService { + + @Autowired + private LogMapper logMapper; + + @Override + public PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime) { + PageHelper.startPage(page, count); + List all = logMapper.query(query, type, startTime, endTime); + return new PageInfo<>(all); + } + + @Override + public void add(LogDto logDto) { + logMapper.add(logDto); + } + + @Override + public int clear() { + return logMapper.clear(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/MediaServerServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/MediaServerServiceImpl.java new file mode 100644 index 0000000..cd70436 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/MediaServerServiceImpl.java @@ -0,0 +1,866 @@ +package com.yfd.monitor.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.CommonCallback; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.InviteStreamType; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.session.SSRCFactory; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.service.IMediaNodeServerService; +import com.yfd.monitor.media.zlm.*; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.ServerKeepaliveData; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.bean.MediaServerLoad; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.service.bean.SSRCInfo; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.MediaServerMapper; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.JsonUtil; +import com.yfd.monitor.utils.redis.RedisUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import javax.annotation.Resource; +import java.io.File; +import java.time.LocalDateTime; +import java.util.*; + +/** + * 媒体服务器节点管理 + */ +@Service +public class MediaServerServiceImpl implements IMediaServerService { + + private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class); + + private final String zlmKeepaliveKeyPrefix = "zlm-keepalive_"; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private SSRCFactory ssrcFactory; + + @Value("${server.ssl.enabled:false}") + private boolean sslEnabled; + + @Value("${server.port}") + private Integer serverPort; + + @Value("${server.appname}") + private String serverAppname; + + @Autowired + private UserSetting userSetting; + + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private MediaServerMapper mediaServerMapper; + + @Resource + private DataSourceTransactionManager dataSourceTransactionManager; + + @Resource + private TransactionDefinition transactionDefinition; + + @Autowired + private Map nodeServerServiceMap; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private EventPublisher publisher; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private SendRtpPortManager sendRtpPortManager; + + /** + * 初始化 + */ + @Override + public void updateVmServer(List mediaServerItemList) { + logger.info("[zlm] 缓存初始化 "); + for (MediaServerItem mediaServerItem : mediaServerItemList) { + if (ObjectUtils.isEmpty(mediaServerItem.getId())) { + continue; + } + // 更新 + if (ssrcFactory.hasMediaServerSSRC(mediaServerItem.getId())) { + ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null); + } + // 查询redis是否存在此mediaServer + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + Boolean hasKey = redisTemplate.hasKey(key); + if (hasKey != null && ! hasKey) { + redisTemplate.opsForValue().set(key, mediaServerItem); + } + + } + } + + + @Override + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, + boolean isPlayback, Integer port, Boolean reUsePort, Integer tcpMode) { + if (mediaServerItem == null || mediaServerItem.getId() == null) { + logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null"); + return null; + } + // 获取mediaServer可用的ssrc + String ssrc; + if (presetSsrc != null) { + ssrc = presetSsrc; + }else { + if (isPlayback) { + ssrc = ssrcFactory.getPlayBackSsrc(mediaServerItem.getId()); + }else { + ssrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); + } + } + + if (streamId == null) { + streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); + } + int rtpServerPort; + if (mediaServerItem.isRtpEnable()) { + rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port, reUsePort, tcpMode); + } else { + rtpServerPort = mediaServerItem.getRtpProxyPort(); + } + return new SSRCInfo(rtpServerPort, ssrc, streamId); + } + + @Override + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId) { + if (mediaServerItem == null) { + return; + } + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId); + } + + @Override + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId, CommonCallback callback) { + if (mediaServerItem == null) { + callback.run(false); + return; + } + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId, callback); + } + + @Override + public void closeRTPServer(String mediaServerId, String streamId) { + MediaServerItem mediaServerItem = this.getOne(mediaServerId); + closeRTPServer(mediaServerItem, streamId); + } + + @Override + public void releaseSsrc(String mediaServerItemId, String ssrc) { + MediaServerItem mediaServerItem = getOne(mediaServerItemId); + if (mediaServerItem == null || ssrc == null) { + return; + } + ssrcFactory.releaseSsrc(mediaServerItemId, ssrc); + } + + /** + * zlm 重启后重置他的推流信息, TODO 给正在使用的设备发送停止命令 + */ + @Override + public void clearRTPServer(MediaServerItem mediaServerItem) { + ssrcFactory.reset(mediaServerItem.getId()); + + } + + + @Override + public void update(MediaServerItem mediaSerItem) { + mediaServerMapper.update(mediaSerItem); + MediaServerItem mediaServerItemInRedis = getOne(mediaSerItem.getId()); + MediaServerItem mediaServerItemInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId()); + if (mediaServerItemInRedis == null || ssrcFactory.hasMediaServerSSRC(mediaSerItem.getId())) { + ssrcFactory.initMediaServerSSRC(mediaServerItemInDataBase.getId(),null); + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItemInDataBase.getId(); + redisTemplate.opsForValue().set(key, mediaServerItemInDataBase); + } + + @Override + public List getAll() { + List result = new ArrayList<>(); + List mediaServerKeys = RedisUtil.scan(redisTemplate, String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + "_" )); + String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + for (Object mediaServerKey : mediaServerKeys) { + String key = (String) mediaServerKey; + MediaServerItem mediaServerItem = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class); + if (Objects.isNull(mediaServerItem)) { + continue; + } + // 检查状态 + Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServerItem.getId()); + if (aDouble != null) { + mediaServerItem.setStatus(true); + } + result.add(mediaServerItem); + } + result.sort((serverItem1, serverItem2)->{ + int sortResult = 0; + LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter); + LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter); + + sortResult = localDateTime1.compareTo(localDateTime2); + return sortResult; + }); + return result; + } + + + @Override + public List getAllFromDatabase() { + return mediaServerMapper.queryAll(); + } + + @Override + public List getAllOnline() { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + Set mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1); + + List result = new ArrayList<>(); + if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) { + for (Object mediaServerId : mediaServerIdSet) { + String mediaServerIdStr = (String) mediaServerId; + String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerIdStr; + result.add((MediaServerItem) redisTemplate.opsForValue().get(serverKey)); + } + } + Collections.reverse(result); + return result; + } + + /** + * 获取单个zlm服务器 + * @param mediaServerId 服务id + * @return MediaServerItem + */ + @Override + public MediaServerItem getOne(String mediaServerId) { + if (mediaServerId == null) { + return null; + } + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId; + return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServerItem.class); + } + + @Override + public MediaServerItem getDefaultMediaServer() { + + return mediaServerMapper.queryDefault(); + } + + @Override + public void clearMediaServerForOnline() { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + redisTemplate.delete(key); + } + + @Override + public void add(MediaServerItem mediaServerItem) { + mediaServerItem.setCreateTime(DateUtil.getNow()); + mediaServerItem.setUpdateTime(DateUtil.getNow()); + mediaServerItem.setHookAliveInterval(30f); + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + if (responseJSON != null) { + JSONArray data = responseJSON.getJSONArray("data"); + if (data != null && data.size() > 0) { + ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); + if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); + } + mediaServerItem.setId(zlmServerConfig.getGeneralMediaServerId()); + zlmServerConfig.setIp(mediaServerItem.getIp()); + mediaServerMapper.add(mediaServerItem); + zlmServerOnline(zlmServerConfig); + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败"); + } + + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败"); + } + } + + @Override + public int addToDatabase(MediaServerItem mediaSerItem) { + return mediaServerMapper.add(mediaSerItem); + } + + @Override + public int updateToDatabase(MediaServerItem mediaSerItem) { + int result = 0; + if (mediaSerItem.isDefaultServer()) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + int delResult = mediaServerMapper.delDefault(); + if (delResult == 0) { + logger.error("移除数据库默认zlm节点失败"); + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return 0; + } + result = mediaServerMapper.add(mediaSerItem); + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }else { + result = mediaServerMapper.update(mediaSerItem); + } + return result; + } + + /** + * 处理zlm上线 + * @param zlmServerConfig zlm上线携带的参数 + */ + @Override + public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { + + MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); + if (serverItem == null) { + logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); + logger.warn("请检查ZLM的配置是否与WVP的一致"); + return; + }else { + logger.info("[ZLM] 正在连接 : {} -> {}:{}", + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); + } + serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval()); + if (serverItem.getHttpPort() == 0) { + serverItem.setHttpPort(zlmServerConfig.getHttpPort()); + } + if (serverItem.getHttpSSlPort() == 0) { + serverItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport()); + } + if (serverItem.getRtmpPort() == 0) { + serverItem.setRtmpPort(zlmServerConfig.getRtmpPort()); + } + if (serverItem.getRtmpSSlPort() == 0) { + serverItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort()); + } + if (serverItem.getRtspPort() == 0) { + serverItem.setRtspPort(zlmServerConfig.getRtspPort()); + } + if (serverItem.getRtspSSLPort() == 0) { + serverItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport()); + } + if (serverItem.getRtpProxyPort() == 0) { + serverItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); + } + serverItem.setStatus(true); + + if (ObjectUtils.isEmpty(serverItem.getId())) { + logger.warn("[未注册的zlm] serverItem缺少ID, 无法接入:{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); + return; + } + mediaServerMapper.update(serverItem); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId(); + if (ssrcFactory.hasMediaServerSSRC(serverItem.getId())) { + ssrcFactory.initMediaServerSSRC(zlmServerConfig.getGeneralMediaServerId(), null); + } + redisTemplate.opsForValue().set(key, serverItem); + resetOnlineServerItem(serverItem); + + + if (serverItem.isAutoConfig()) { + // 查看assist服务的录像路径配置 + if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) { + JSONObject info = assistRESTfulUtils.getInfo(serverItem, null); + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) { + JSONObject dataJson = info.getJSONObject("data"); + if (dataJson != null) { + String recordPath = dataJson.getString("record"); + userSetting.setRecordPath(recordPath); + } + } + } + setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); + } + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId(); + dynamicTask.stop(zlmKeepaliveKey); + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000); + publisher.zlmOnlineEventPublish(serverItem.getId()); + + logger.info("[ZLM] 连接成功 {} - {}:{} ", + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); + } + + class KeepAliveTimeoutRunnable implements Runnable{ + + private MediaServerItem serverItem; + + public KeepAliveTimeoutRunnable(MediaServerItem serverItem) { + this.serverItem = serverItem; + } + + @Override + public void run() { + logger.info("[zlm心跳到期]:" + serverItem.getId()); + // 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理 + JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(serverItem); + if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) { + logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息,请检查zlm是否可以正常向wvp发送心跳", serverItem.getId()); + // 添加zlm信息 + updateMediaServerKeepalive(serverItem.getId(), null); + }else { + publisher.zlmOfflineEventPublish(serverItem.getId()); + } + } + } + + + @Override + public void zlmServerOffline(String mediaServerId) { + delete(mediaServerId); + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerId; + dynamicTask.stop(zlmKeepaliveKey); + } + + @Override + public void resetOnlineServerItem(MediaServerItem serverItem) { + // 更新缓存 + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + // 使用zset的分数作为当前并发量, 默认值设置为0 + if (redisTemplate.opsForZSet().score(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置 + redisTemplate.opsForZSet().add(key, serverItem.getId(), 0L); + // 查询服务流数量 + zlmresTfulUtils.getMediaList(serverItem, null, null, "rtsp",(mediaList ->{ + System.out.println("查询服务流数量"+mediaList.toString()); + Integer code = mediaList.getInteger("code"); + if (code == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data != null) { + redisTemplate.opsForZSet().add(key, serverItem.getId(), data.size()); + } + } + })); + }else { + clearRTPServer(serverItem); + } + } + + + @Override + public void addCount(String mediaServerId) { + if (mediaServerId == null) { + return; + } + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + redisTemplate.opsForZSet().incrementScore(key, mediaServerId, 1); + + + } + + @Override + public void removeCount(String mediaServerId) { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + redisTemplate.opsForZSet().incrementScore(key, mediaServerId, - 1); + } + + /** + * 获取负载最低的节点 + * @return MediaServerItem + */ + @Override + public MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist) { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + Long size = redisTemplate.opsForZSet().zCard(key); + if (size == null || size == 0) { + logger.info("获取负载最低的节点时无在线节点"); + return null; + } + + // 获取分数最低的,及并发最低的 + Set objects = redisTemplate.opsForZSet().range(key, 0, -1); + ArrayList mediaServerObjectS = new ArrayList<>(objects); + MediaServerItem mediaServerItem = null; + if (hasAssist == null) { + String mediaServerId = (String)mediaServerObjectS.get(0); + mediaServerItem = getOne(mediaServerId); + }else if (hasAssist) { + for (Object mediaServerObject : mediaServerObjectS) { + String mediaServerId = (String)mediaServerObject; + MediaServerItem serverItem = getOne(mediaServerId); + if (serverItem.getRecordAssistPort() > 0) { + mediaServerItem = serverItem; + break; + } + } + }else if (!hasAssist) { + for (Object mediaServerObject : mediaServerObjectS) { + String mediaServerId = (String)mediaServerObject; + MediaServerItem serverItem = getOne(mediaServerId); + if (serverItem.getRecordAssistPort() == 0) { + mediaServerItem = serverItem; + break; + } + } + } + + return mediaServerItem; + } + + /** + * 对zlm服务器进行基础配置 + * @param mediaServerItem 服务ID + * @param restart 是否重启zlm + */ + @Override + public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { + logger.info("[ZLM] 正在设置 :{} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + String protocol = sslEnabled ? "https" : "http"; + String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); + if(StrUtil.isNotEmpty(serverAppname)){ + hookPrex = String.format("%s://%s:%s/%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort,serverAppname); + } + + Map param = new HashMap<>(); + param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline + if (mediaServerItem.getRtspPort() != 0) { + param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s"); + } + param.put("hook.enable","1"); + param.put("hook.on_flow_report",""); + param.put("hook.on_play",String.format("%s/on_play", hookPrex)); + param.put("hook.on_http_access",""); + param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); + param.put("hook.on_record_ts",""); + param.put("hook.on_rtsp_auth",""); + param.put("hook.on_rtsp_realm",""); + param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); + param.put("hook.on_shell_login",""); + param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); + param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); + param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); + param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex)); + param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex)); + param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex)); + if (mediaServerItem.getRecordAssistPort() > 0) { + param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort())); + }else { + param.put("hook.on_record_mp4",""); + } + param.put("hook.timeoutSec","20"); + // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。 + // 置0关闭此特性(推流断开会导致立即断开播放器) + // 此参数不应大于播放器超时时间 + // 优化此消息以更快的收到流注销事件 + param.put("protocol.continue_push_ms", "3000" ); + // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流, + // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 +// param.put("general.wait_track_ready_ms", "3000" ); + if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) { + param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-")); + } + + if (userSetting.getRecordPath() != null) { + File recordPathFile = new File(userSetting.getRecordPath()); + File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile(); + param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile()); + param.put("record.appName", recordPathFile.getName()); + } + + JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param); + + if (responseJSON != null && responseJSON.getInteger("code") == 0) { + if (restart) { + logger.info("[ZLM] 设置成功,开始重启以保证配置生效 {} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + zlmresTfulUtils.restartServer(mediaServerItem); + }else { + logger.info("[ZLM] 设置成功 {} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + } + + + }else { + logger.info("[ZLM] 设置zlm失败 {} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + } + + + } + + + @Override + public MediaServerItem checkMediaServer(String ip, int port, String secret) { + if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在"); + } + MediaServerItem mediaServerItem = new MediaServerItem(); + mediaServerItem.setIp(ip); + mediaServerItem.setHttpPort(port); + mediaServerItem.setSecret(secret); + JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + if (responseJSON == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败"); + } + JSONArray data = responseJSON.getJSONArray("data"); + ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); + if (zlmServerConfig == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败"); + } + if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); + } + mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpPort()); + mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort()); + mediaServerItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort()); + mediaServerItem.setRtspPort(zlmServerConfig.getRtspPort()); + mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport()); + mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); + mediaServerItem.setStreamIp(ip); + mediaServerItem.setHookIp(sipConfig.getIp().split(",")[0]); + mediaServerItem.setSdpIp(ip); + return mediaServerItem; + } + + @Override + public boolean checkMediaRecordServer(String ip, int port) { + boolean result = false; + OkHttpClient client = new OkHttpClient(); + String url = String.format("http://%s:%s/index/api/record", ip, port); + Request request = new Request.Builder() + .get() + .url(url) + .build(); + try { + Response response = client.newCall(request).execute(); + if (response != null) { + result = true; + } + } catch (Exception e) {} + + return result; + } + + @Override + public void delete(String id) { + redisTemplate.opsForZSet().remove(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), id); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + id; + redisTemplate.delete(key); + } + @Override + public void deleteDb(String id){ + //同步删除数据库中的数据 + mediaServerMapper.delOne(id); + } + + @Override + public void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data) { + MediaServerItem mediaServerItem = getOne(mediaServerId); + if (mediaServerItem == null) { + // 缓存不存在,从数据库查询,如果数据库不存在则是错误的 + mediaServerItem = getOneFromDatabase(mediaServerId); + if (mediaServerItem == null) { + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息"); + return; + } + // zlm连接重试 + logger.warn("[更新ZLM 保活信息]尝试链接zml id {}", mediaServerId); + ssrcFactory.initMediaServerSSRC(mediaServerItem.getId(), null); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + redisTemplate.opsForValue().set(key, mediaServerItem); + clearRTPServer(mediaServerItem); + } + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId(); + dynamicTask.stop(zlmKeepaliveKey); + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(mediaServerItem), (mediaServerItem.getHookAliveInterval().intValue() + 5) * 1000); + } + + private MediaServerItem getOneFromDatabase(String mediaServerId) { + return mediaServerMapper.queryOne(mediaServerId); + } + + @Override + public void syncCatchFromDatabase() { + List allInCatch = getAll(); + List allInDatabase = mediaServerMapper.queryAll(); + Map mediaServerItemMap = new HashMap<>(); + + for (MediaServerItem mediaServerItem : allInDatabase) { + mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem); + } + for (MediaServerItem mediaServerItem : allInCatch) { + if (!mediaServerItemMap.containsKey(mediaServerItem.getId())) { + delete(mediaServerItem.getId()); + } + } + } + + @Override + public boolean checkRtpServer(MediaServerItem mediaServerItem, String app, String stream) { + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, stream); + if(rtpInfo.getInteger("code") == 0){ + return rtpInfo.getBoolean("exist"); + } + return false; + } + + @Override + public MediaServerLoad getLoad(MediaServerItem mediaServerItem) { + MediaServerLoad result = new MediaServerLoad(); + result.setId(mediaServerItem.getId()); + result.setPush(redisCatchStorage.getPushStreamCount(mediaServerItem.getId())); + result.setProxy(redisCatchStorage.getProxyStreamCount(mediaServerItem.getId())); + result.setGbReceive(redisCatchStorage.getGbReceiveCount(mediaServerItem.getId())); + result.setGbSend(redisCatchStorage.getGbSendCount(mediaServerItem.getId())); + return result; + } + + @Override + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServer, String app, String stream, MediaInfo mediaInfo, String addr, String callId, boolean isPlay) { + + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setStream(stream); + streamInfoResult.setApp(app); + if (addr == null) { + addr = mediaServer.getStreamIp(); + } + + streamInfoResult.setIp(addr); + streamInfoResult.setMediaServerId(mediaServer.getId()); + String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; + streamInfoResult.setRtmp(addr, mediaServer.getRtmpPort(),mediaServer.getRtmpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtsp(addr, mediaServer.getRtspPort(),mediaServer.getRtspSSLPort(), app, stream, callIdParam); + + +// if ("abl".equals(mediaServer.getType())) { +// String flvFile = String.format("%s/%s.flv%s", app, stream, callIdParam); +// streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile); +// streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile); +// }else { +// String flvFile = String.format("%s/%s.live.flv%s", app, stream, callIdParam); +// streamInfoResult.setFlv(addr, mediaServer.getFlvPort(),mediaServer.getFlvSSLPort(), flvFile); +// streamInfoResult.setWsFlv(addr, mediaServer.getWsFlvPort(),mediaServer.getWsFlvSSLPort(), flvFile); +// } + + streamInfoResult.setFmp4(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setHls(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setTs(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtc(addr, mediaServer.getHttpPort(),mediaServer.getHttpSSlPort(), app, stream, callIdParam, isPlay); + +// streamInfoResult.setMediaInfo(mediaInfo); + return streamInfoResult; + } + + @Override + public Boolean isStreamReady(MediaServerItem mediaServer, String rtp, String streamId) { + + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[isStreamReady] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return false; + } + MediaInfo mediaInfo = mediaNodeServerService.getMediaInfo(mediaServer, rtp, streamId); + return mediaInfo != null; + } + + + + @Override + public boolean stopSendRtp(MediaServerItem mediaInfo, String app, String stream, String ssrc) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaInfo.getType()); + if (mediaNodeServerService == null) { + logger.info("[stopSendRtp] 失败, mediaServer的类型: {},未找到对应的实现类", mediaInfo.getType()); + return false; + } + return mediaNodeServerService.stopSendRtp(mediaInfo, app, stream, ssrc); + } + @Override + public void startSendRtpPassive(MediaServerItem mediaServer, ParentPlatform platform, SendRtpItem sendRtpItem, Integer timeout) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + logger.info("[startSendRtpPassive] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到mediaServer对应的实现类"); + } + mediaNodeServerService.startSendRtpPassive(mediaServer, sendRtpItem, timeout); + sendPlatformStartPlayMsg(platform, sendRtpItem); + } + + @Override + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, + MediaInfo mediaInfo, String callId) { + return getStreamInfoByAppAndStream(mediaServerItem, app, stream, mediaInfo, null, callId, true); + } + + @Override + public SendRtpItem createSendRtpItem(MediaServerItem mediaServerItem, String ip, int port, String ssrc, + String requesterId, String deviceId, String channelId, + boolean isTcp, boolean rtcp) { + int localPort = sendRtpPortManager.getNextPort(mediaServerItem); + if (localPort == 0) { + return null; + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setIp(ip); + sendRtpItem.setPort(port); + sendRtpItem.setSsrc(ssrc); + sendRtpItem.setPlatformId(deviceId); + sendRtpItem.setDeviceId(deviceId); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setTcp(isTcp); + sendRtpItem.setRtcp(rtcp); + sendRtpItem.setApp("rtp"); + sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); + sendRtpItem.setMediaServerId(mediaServerItem.getId()); + return sendRtpItem; + } + + private void sendPlatformStartPlayMsg(ParentPlatform platform, SendRtpItem sendRtpItem) { + if (sendRtpItem.getPlayType() == InviteStreamType.PUSH && platform != null) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, sendRtpItem.getApp(), sendRtpItem.getStreamId(), + sendRtpItem.getChannelId(), platform.getServerGBId(), platform.getName(), userSetting.getServerId(), + sendRtpItem.getMediaServerId()); +// messageForPushChannel.setPlatFormIndex(platform.getId()); + redisCatchStorage.sendPlatformStartPlayMsg(messageForPushChannel); + } + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/MediaServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/MediaServiceImpl.java new file mode 100644 index 0000000..921e2bb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/MediaServiceImpl.java @@ -0,0 +1,113 @@ +package com.yfd.monitor.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.MediaConfig; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamAuthorityInfo; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IMediaService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +@Service +public class MediaServiceImpl implements IMediaService { + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IMediaServerService mediaServerService; + + + @Autowired + private MediaConfig mediaConfig; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + + + @Override + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String callId) { + return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null, callId); + } + + + + + @Override + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) { + StreamInfo streamInfo = null; + if (mediaServerId == null) { + mediaServerId = mediaConfig.getId(); + } + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo == null) { + return null; + } + String calld = null; + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); + if (streamAuthorityInfo != null) { + calld = streamAuthorityInfo.getCallId(); + } + JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, app, stream); + if (mediaList != null) { + if (mediaList.getInteger("code") == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data == null) { + return null; + } + JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); + JSONArray tracks = mediaJSON.getJSONArray("tracks"); + if (authority) { + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld); + }else { + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null); + } + } + } + return streamInfo; + } + + + + @Override + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) { + return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority); + } + + @Override + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId) { + StreamInfo streamInfoResult = new StreamInfo(); + streamInfoResult.setStream(stream); + streamInfoResult.setApp(app); + if (addr == null) { + addr = mediaInfo.getStreamIp(); + } + + streamInfoResult.setIp(addr); + streamInfoResult.setMediaServerId(mediaInfo.getId()); + String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; + streamInfoResult.setRtmp(addr, mediaInfo.getRtmpPort(),mediaInfo.getRtmpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtsp(addr, mediaInfo.getRtspPort(),mediaInfo.getRtspSSLPort(), app, stream, callIdParam); + streamInfoResult.setFlv(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setFmp4(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setHls(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setTs(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtc(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + + streamInfoResult.setTracks(tracks); + return streamInfoResult; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlatformChannelServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlatformChannelServiceImpl.java new file mode 100644 index 0000000..a3993ad --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlatformChannelServiceImpl.java @@ -0,0 +1,171 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.service.IPlatformChannelService; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.ParentPlatformMapper; +import com.yfd.monitor.storager.dao.PlatformCatalogMapper; +import com.yfd.monitor.storager.dao.PlatformChannelMapper; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@Service +public class PlatformChannelServiceImpl implements IPlatformChannelService { + + private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class); + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + private SubscribeHolder subscribeHolder; + + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private PlatformCatalogMapper catalogManager; + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + EventPublisher eventPublisher; + + @Override + public int updateChannelForGB(String platformId, List channelReduces, String catalogId) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + logger.warn("更新级联通道信息时未找到平台{}的信息", platformId); + return 0; + } + Map deviceAndChannels = new HashMap<>(); + for (ChannelReduce channelReduce : channelReduces) { + channelReduce.setCatalogId(catalogId); + deviceAndChannels.put(channelReduce.getId(), channelReduce); + } + List deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet()); + // 查询当前已经存在的 + List channelIds = platformChannelMapper.findChannelRelatedPlatform(platformId, channelReduces); + if (deviceAndChannelList != null) { + deviceAndChannelList.removeAll(channelIds); + } + for (Integer channelId : channelIds) { + deviceAndChannels.remove(channelId); + } + List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values()); + // 对剩下的数据进行存储 + int allCount = 0; + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + int limitCount = 300; + if (channelReducesToAdd.size() > 0) { + if (channelReducesToAdd.size() > limitCount) { + for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channelReducesToAdd.size()) { + toIndex = channelReducesToAdd.size(); + } + int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex)); + result = result || count < 0; + allCount += count; + logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex); + } + }else { + allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd); + result = result || allCount < 0; + logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size()); + } + + if (result) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + allCount = 0; + }else { + logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId); + dataSourceTransactionManager.commit(transactionStatus); + + } + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId); + if (catalogSubscribe != null) { + List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform); + if (deviceChannelList != null) { + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); + } + } + logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId); + } + return allCount; + } + + private List getDeviceChannelListByChannelReduceList(List channelReduces, String catalogId, ParentPlatform platform) { + List deviceChannelList = new ArrayList<>(); + if (channelReduces.size() > 0){ + PlatformCatalog catalog = catalogManager.selectByPlatFormAndCatalogId(platform.getServerGBId(),catalogId); + if (catalog == null && catalogId.equals(platform.getDeviceGBId())) { + for (ChannelReduce channelReduce : channelReduces) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); + deviceChannel.setParental(0); + deviceChannel.setCivilCode(platform.getServerGBDomain()); + deviceChannelList.add(deviceChannel); + } + return deviceChannelList; + } else if (catalog == null || !catalogId.equals(platform.getDeviceGBId())) { + logger.warn("未查询到目录{}的信息", catalogId); + return null; + } + for (ChannelReduce channelReduce : channelReduces) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); + deviceChannel.setParental(0); + deviceChannel.setCivilCode(catalog.getCivilCode()); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + deviceChannelList.add(deviceChannel); + } + } + return deviceChannelList; + } + + @Override + public int delAllChannelForGB(String platformId, String catalogId) { + + int result; + if (platformId == null) { + return 0; + } + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + return 0; + } + if (ObjectUtils.isEmpty(catalogId)) { + catalogId = null; + } + + List deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId); + eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL); + + return platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlatformServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlatformServiceImpl.java new file mode 100644 index 0000000..4cc1c72 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlatformServiceImpl.java @@ -0,0 +1,459 @@ +package com.yfd.monitor.service.impl; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.SSRCFactory; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IPlatformService; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.*; +import com.yfd.monitor.utils.DateUtil; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import gov.nist.javax.sip.header.WWWAuthenticate; +import org.apache.ibatis.ognl.ObjectNullHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Service; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.sip.header.WWWAuthenticateHeader; +import javax.sip.message.Request; +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + + +@Service +public class PlatformServiceImpl implements IPlatformService { + + private final static String REGISTER_KEY_PREFIX = "platform_register_"; + + private final static String REGISTER_FAIL_AGAIN_KEY_PREFIX = "platform_register_fail_again_"; + private final static String KEEPALIVE_KEY_PREFIX = "platform_keepalive_"; + +// private final static String REGISTER_FAIL_AGAIN_KEY_PREFIX = "platform_register_fail_again_"; + + private final static Logger logger = LoggerFactory.getLogger(PlatformServiceImpl.class); + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SIPCommanderFroPlatform commanderForPlatform; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZLMRTPServerFactory zlmServerFactory; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private UserSetting userSetting; + + + @Override + public ParentPlatform queryPlatformByServerGBId(String platformGbId) { + return platformMapper.getParentPlatByServerGBId(platformGbId); + } + + @Override + public PageInfo queryParentPlatformList(int page, int count) { + PageHelper.startPage(page, count); + List all = platformMapper.getParentPlatformList(); + return new PageInfo<>(all); + } + + @Override + public boolean add(ParentPlatform parentPlatform) { + + if (parentPlatform.getCatalogGroup() == 0) { + // 每次发送目录的数量默认为1 + parentPlatform.setCatalogGroup(1); + } + if (parentPlatform.getAdministrativeDivision() == null) { + // 行政区划默认去编号的前6位 + parentPlatform.setAdministrativeDivision(parentPlatform.getServerGBId().substring(0, 6)); + } + parentPlatform.setCatalogId(parentPlatform.getDeviceGBId()); + int result = platformMapper.addParentPlatform(parentPlatform); + // 添加缓存 + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + parentPlatformCatch.setParentPlatform(parentPlatform); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + if (parentPlatform.isEnable()) { + // 保存时启用就发送注册 + // 注册成功时由程序直接调用了online方法 + try { + commanderForPlatform.register(parentPlatform, eventResult -> { + logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId()); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联: {}", e.getMessage()); + } + } + return result > 0; + } + + @Override + public boolean update(ParentPlatform parentPlatform) { + logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId()); + parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase()); + ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId()); + ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId()); + parentPlatform.setUpdateTime(DateUtil.getNow()); + + // 停止心跳定时 + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId(); + dynamicTask.stop(keepaliveTaskKey); + // 停止注册定时 + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId(); + dynamicTask.stop(registerTaskKey); + // 注销旧的 + try { + if (parentPlatformOld.isStatus() && parentPlatformCatchOld != null) { + logger.info("保存平台{}时发现旧平台在线,发送注销命令", parentPlatformOld.getServerGBId()); + commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> { + logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId()); + }); + } + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + + // 更新数据库 + if (parentPlatform.getCatalogGroup() == 0) { + parentPlatform.setCatalogGroup(1); + } + if (parentPlatform.getAdministrativeDivision() == null) { + parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision()); + } + + platformMapper.updateParentPlatform(parentPlatform); + // 更新redis + redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId()); + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + // 注册 + if (parentPlatform.isEnable()) { + // 保存时启用就发送注册 + // 注册成功时由程序直接调用了online方法 + try { + logger.info("[国标级联] 平台注册 {}", parentPlatform.getDeviceGBId()); + commanderForPlatform.register(parentPlatform, eventResult -> { + logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId()); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联: {}", e.getMessage()); + } + } + + + return false; + } + + + @Override + public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) { + logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId()); + final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + parentPlatform.getServerGBId(); + dynamicTask.stop(registerFailAgainTaskKey); + + platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + if (parentPlatformCatch == null) { + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + parentPlatform.setStatus(true); + parentPlatformCatch.setParentPlatform(parentPlatform); + } + + parentPlatformCatch.getParentPlatform().setStatus(true); + parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + logger.info("================================================================================="); + logger.info("=================================================================================" + parentPlatform.toString()); + if (!dynamicTask.isAlive(registerTaskKey)) { + logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId()); + // 添加注册任务 + dynamicTask.startCron(registerTaskKey, + // 注册失败(注册成功时由程序直接调用了online方法) + () -> registerTask(parentPlatform, sipTransactionInfo), + parentPlatform.getExpires() * 1000); + } + + +// final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); +// if (!dynamicTask.contains(keepaliveTaskKey)) { +// logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId()); +// // 添加心跳任务 +// dynamicTask.startCron(keepaliveTaskKey, +// ()-> { +// try { +// commanderForPlatform.keepalive(parentPlatform, eventResult -> { +// // 心跳失败 +// if (eventResult.type != SipSubscribe.EventResultType.timeout) { +// logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg); +// } +// // 心跳失败 +// ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); +// // 此时是第三次心跳超时, 平台离线 +// if (platformCatch.getKeepAliveReply() == 2) { +// // 设置平台离线,并重新注册 +// logger.info("[国标级联] 三次心跳失败, 平台{}({})离线", parentPlatform.getName(), parentPlatform.getServerGBId()); +// offline(parentPlatform, false); +// }else { +// platformCatch.setKeepAliveReply(platformCatch.getKeepAliveReply() + 1); +// redisCatchStorage.updatePlatformCatchInfo(platformCatch); +// } +// +// }, eventResult -> { +// // 心跳成功 +// // 清空之前的心跳超时计数 +// ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); +// if (platformCatch != null && platformCatch.getKeepAliveReply() > 0) { +// platformCatch.setKeepAliveReply(0); +// redisCatchStorage.updatePlatformCatchInfo(platformCatch); +// } +// logger.info("[发送心跳] 国标级联 发送心跳, code: {}, msg: {}", eventResult.statusCode, eventResult.msg); +// }); +// } catch (SipException | InvalidArgumentException | ParseException e) { +// logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); +// } +// }, +// (parentPlatform.getKeepTimeout())*1000); +// } +// if (parentPlatform.isAutoPushChannel()) { +// if (subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()) == null) { +// logger.info("[国标级联]:{}, 添加自动通道推送模拟订阅信息", parentPlatform.getServerGBId()); +// addSimulatedSubscribeInfo(parentPlatform); +// } +// }else { +// SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); +// if (catalogSubscribe != null && catalogSubscribe.getExpires() == -1) { +// subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId()); +// } +// } + } + + @Override + public void addSimulatedSubscribeInfo(ParentPlatform parentPlatform) { + // 自动添加一条模拟的订阅信息 + SubscribeInfo subscribeInfo = new SubscribeInfo(); + subscribeInfo.setId(parentPlatform.getServerGBId()); + subscribeInfo.setExpires(-1); + subscribeInfo.setEventType("Catalog"); + int random = (int) Math.floor(Math.random() * 10000); + subscribeInfo.setEventId(random + ""); + subscribeInfo.setSimulatedCallId(UUID.randomUUID().toString().replace("-", "") + "@" + parentPlatform.getServerIP()); + subscribeInfo.setSimulatedFromTag(UUID.randomUUID().toString().replace("-", "")); + subscribeInfo.setSimulatedToTag(UUID.randomUUID().toString().replace("-", "")); + subscribeHolder.putCatalogSubscribe(parentPlatform.getServerGBId(), subscribeInfo); + } + + private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) { + try { + // 不在同一个会话中续订则每次全新注册 + if (!userSetting.isRegisterKeepIntDialog()) { + sipTransactionInfo = null; + } + + if (sipTransactionInfo == null) { + logger.info("[国标级联] 平台:{}注册即将到期,开始重新注册", parentPlatform.getServerGBId()); + } else { + logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId()); + } + Object object = redisCatchStorage.queryPlatformWWwInfo(parentPlatform.getServerGBId()); + JSONObject job = JSONUtil.parseObj(object); + WWWAuthenticateHeader www = new WWWAuthenticate(); + www.setAlgorithm(job.getJSONObject("parameters").getJSONObject("algorithm").getStr("algorithm")); + www.setRealm(job.getJSONObject("parameters").getJSONObject("realm").getStr("realm")); +// www.setQop(job.getJSONObject("parameters").getJSONObject("qop").getStr("qop")); + www.setNonce(job.getJSONObject("parameters").getJSONObject("nonce").getStr("nonce")); + commanderForPlatform.register(parentPlatform, sipTransactionInfo, www, eventResult -> { + logger.info("[国标级联] 平台:{}注册失败,{}:{}", parentPlatform.getServerGBId(), + eventResult.statusCode, eventResult.msg); + offline(parentPlatform, false); + }, null, true); + + commanderForPlatform.sendCatalogNotice(parentPlatform, sipTransactionInfo); + } catch (Exception e) { + e.printStackTrace(); + logger.error("[命令发送失败] 国标级联定时注册: {}", e.getMessage()); + } + } + + @Override + public void offline(ParentPlatform parentPlatform, boolean stopRegister) { + logger.info("[平台离线]:{}", parentPlatform.getServerGBId()); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + parentPlatformCatch.setKeepAliveReply(0); + parentPlatformCatch.setRegisterAliveReply(0); + ParentPlatform parentPlatformInCatch = parentPlatformCatch.getParentPlatform(); + parentPlatformInCatch.setStatus(false); + parentPlatformCatch.setParentPlatform(parentPlatformInCatch); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), false); + + // 停止所有推流 + logger.info("[平台离线] {}, 停止所有推流", parentPlatform.getServerGBId()); + stopAllPush(parentPlatform.getServerGBId()); + + // 清除注册定时 + logger.info("[平台离线] {}, 停止定时注册任务", parentPlatform.getServerGBId()); + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + if (dynamicTask.contains(registerTaskKey)) { + dynamicTask.stop(registerTaskKey); + } + // 清除心跳定时 + logger.info("[平台离线] {}, 停止定时发送心跳任务", parentPlatform.getServerGBId()); + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); + if (dynamicTask.contains(keepaliveTaskKey)) { + // 清除心跳任务 + dynamicTask.stop(keepaliveTaskKey); + } + // 停止订阅回复 + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); + if (catalogSubscribe != null) { + if (catalogSubscribe.getExpires() > 0) { + logger.info("[平台离线] {}, 停止目录订阅回复", parentPlatform.getServerGBId()); + subscribeHolder.removeCatalogSubscribe(parentPlatform.getServerGBId()); + } + } + logger.info("[平台离线] {}, 停止移动位置订阅回复", parentPlatform.getServerGBId()); + subscribeHolder.removeMobilePositionSubscribe(parentPlatform.getServerGBId()); + // 发起定时自动重新注册 + if (!stopRegister) { + // 设置为60秒自动尝试重新注册 + final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + parentPlatform.getServerGBId(); + ParentPlatform platform = platformMapper.getParentPlatById(parentPlatform.getId()); + if (platform.isEnable()) { + dynamicTask.startCron(registerFailAgainTaskKey, + () -> registerTask(platform, null), + userSetting.getRegisterAgainAfterTime() * 1000); + } + + } + } + + private void stopAllPush(String platformId) { + List sendRtpItems = redisCatchStorage.querySendRTPServer(platformId); + if (sendRtpItems != null && sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); + redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null); + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + Map param = new HashMap<>(3); + param.put("vhost", "__defaultVhost__"); + param.put("app", sendRtpItem.getApp()); + param.put("stream", sendRtpItem.getStreamId()); + zlmServerFactory.stopSendRtpStream(mediaInfo, param); + } + } + } + + @Override + public void login(ParentPlatform parentPlatform) { + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + try { + commanderForPlatform.register(parentPlatform, eventResult1 -> { + logger.info("[国标级联] {},开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId()); + // 添加注册任务 + dynamicTask.startCron(registerTaskKey, + // 注册失败(注册成功时由程序直接调用了online方法) + () -> logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()), + 60 * 1000); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联注册: {}", e.getMessage()); + } + } + + @Override + public void logout(ParentPlatform parentPlatform) { + + try { + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + Object object = redisCatchStorage.queryPlatformWWwInfo(parentPlatform.getServerGBId()); + JSONObject job = JSONUtil.parseObj(object); + WWWAuthenticateHeader www = new WWWAuthenticate(); + www.setAlgorithm(job.getJSONObject("parameters").getJSONObject("algorithm").getStr("algorithm")); + www.setRealm(job.getJSONObject("parameters").getJSONObject("realm").getStr("realm")); + www.setQop(job.getJSONObject("parameters").getJSONObject("qop").getStr("qop")); + www.setNonce(job.getJSONObject("parameters").getJSONObject("nonce").getStr("nonce")); + www.setParameter("Logout-Reason", "maintenance"); + commanderForPlatform.register(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), www, null, (eventResult) -> { + logger.info("[向上级主动注销成功! {}", parentPlatform.getDeviceGBId()); + this.offline(parentPlatform, true); + }, false); + + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[向上级主动 注销 失败! {}", e.getMessage()); + } + } + + @Override + public void sendNotifyMobilePosition(String platformId) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformId); + if (platform == null) { + return; + } + SubscribeInfo subscribe = subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()); + if (subscribe != null) { + + // TODO 暂时只处理视频流的回复,后续增加对国标设备的支持 + List gbStreams = gbStreamMapper.queryGbStreamListInPlatform(platform.getServerGBId(), userSetting.isUsePushingAsStatus()); + if (gbStreams.size() == 0) { + return; + } + for (DeviceChannel deviceChannel : gbStreams) { + String gbId = deviceChannel.getChannelId(); + GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId); + // 无最新位置不发送 + if (gpsMsgInfo != null) { + // 经纬度都为0不发送 + if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) { + continue; + } + + } + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlayServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlayServiceImpl.java new file mode 100644 index 0000000..62ab5e7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/PlayServiceImpl.java @@ -0,0 +1,1512 @@ +package com.yfd.monitor.service.impl; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.*; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.exception.ServiceException; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.SipSubscribe; +import com.yfd.monitor.gdw2019.session.AudioBroadcastManager; +import com.yfd.monitor.gdw2019.session.SSRCFactory; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.yfd.monitor.gdw2019.utils.SipUtils; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.event.hook.HookSubscribe; +import com.yfd.monitor.media.event.media.MediaArrivalEvent; +import com.yfd.monitor.media.zlm.AssistRESTfulUtils; +import com.yfd.monitor.media.zlm.SendRtpPortManager; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.HookSubscribeFactory; +import com.yfd.monitor.media.zlm.dto.HookSubscribeForStreamChange; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.*; +import com.yfd.monitor.service.bean.*; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.AudioBroadcastResult; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import com.yfd.monitor.vmanager.gdw2019.play.bean.AudioBroadcastEvent; +import gov.nist.javax.sip.message.SIPResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sdp.*; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.ParseException; +import java.util.*; + +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +@Service +public class PlayServiceImpl implements IPlayService { + + private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SendRtpPortManager sendRtpPortManager; + + @Autowired + private IInviteStreamService inviteStreamService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + + @Autowired + private IMediaService mediaService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private AudioBroadcastManager audioBroadcastManager; + + @Autowired + private VideoStreamSessionManager streamSession; + + + @Autowired + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private SSRCFactory ssrcFactory; + + @Autowired + private RedisTemplate redisTemplate; + + + /** + * 流到来的处理 + */ + @Async("taskExecutor") + @org.springframework.context.event.EventListener + public void onApplicationEvent(MediaArrivalEvent event) { + if ("broadcast".equals(event.getApp())) { + if (event.getStream().indexOf("_") > 0) { + String[] streamArray = event.getStream().split("_"); + if (streamArray.length == 2) { + String deviceId = streamArray[0]; + String channelId = streamArray[1]; + Device device = deviceService.getDevice(deviceId); + if (device == null) { + logger.info("[语音对讲/喊话] 未找到设备:{}", deviceId); + return; + } + if ("broadcast".equals(event.getApp())) { + if (audioBroadcastManager.exit(deviceId, channelId)) { + stopAudioBroadcast(deviceId, channelId); + } + // 开启语音对讲通道 + try { + audioBroadcastCmd(device, channelId, event.getMediaServer(), + event.getApp(), event.getStream(), 60, false, (msg) -> { + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); + }); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 语音对讲: {}", e.getMessage()); + } + }else if ("talk".equals(event.getApp())) { + // 开启语音对讲通道 + talkCmd(device, channelId, event.getMediaServer(), event.getStream(), (msg) -> { + logger.info("[语音对讲] 通道建立成功, device: {}, channel: {}", deviceId, channelId); + }); + } + } + } + } + + + } + + + @Override + public void play(MediaServerItem mediaServerItem, String deviceId, String channelId, + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + Runnable timeoutCallback) { + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); + } + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + + Device device = redisCatchStorage.getDevice(deviceId); + if(device==null){ + return; + } + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); + + if (streamInfo != null) { + String streamId = streamInfo.getStream(); + if (streamId == null) { + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播失败, redis缓存streamId等于null"); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + return; + } + String mediaServerId = streamInfo.getMediaServerId(); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); + if (rtpInfo.getInteger("code") == 0) { + if (rtpInfo.getBoolean("exist")) { + int localPort = rtpInfo.getInteger("local_port"); + if (localPort == 0) { + logger.warn("[点播],点播时发现rtpServerC存在,但是尚未开始推流"); + // 此时说明rtpServer已经创建但是流还没有推上来 + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播已经在进行中,请稍候重试"); + msg.setData(wvpResult); + + resultHolder.invokeAllResult(msg); + return; + } else { + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + wvpResult.setData(streamInfo); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + if (hookEvent != null) { + hookEvent.response(mediaServerItem, JSON.parseObject(JSON.toJSONString(streamInfo))); + } + } + + } else { + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + streamInfo = null; + } + } else { + //zlm连接失败 + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + streamInfo = null; + + } + } + if (streamInfo == null) { + String streamId = null; + if (mediaServerItem.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false, 0, false, device.getStreamModeForParam()); + if (ssrcInfo == null) { + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("开启收流失败"); + msg.setData(wvpResult); + + resultHolder.invokeAllResult(msg); + return; + } + play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> { + if (hookEvent != null) { + hookEvent.response(mediaServerItem, response); + } + }, event -> { + // sip error错误 + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg)); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + if (errorEvent != null) { + errorEvent.response(event); + } + }, (code, msgStr) -> { + // invite点播超时 + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + if (code == 0) { + wvpResult.setMsg("点播超时,请稍候重试"); + } else if (code == 1) { + wvpResult.setMsg("收流超时,请稍候重试"); + } + msg.setData(wvpResult); + // 回复之前所有的点播请求 + resultHolder.invokeAllResult(msg); + }); + } + } + + + @Override + public DeferredResult> play_subStream(MediaServerItem mediaServerItem, String deviceId, + String channelId) { + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId + "_sub"; + String stream = String.format("%s_%s", deviceId, channelId + "_sub"); + List patrolDeviceList = storager.getRiisPatrolDeviceByCode(deviceId); + DeferredResult> result = + new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + if (patrolDeviceList.size() > 0) { + Map map = patrolDeviceList.get(0); + String channelinfo = map.get("channelinfo").toString(); + cn.hutool.json.JSONArray jsonArray = JSONUtil.parseArray(channelinfo); + String url = ""; + System.out.println("========================channelId===============================" + channelId); + for (int i = 0; i < jsonArray.size(); i++) { + cn.hutool.json.JSONObject jsonObject = jsonArray.getJSONObject(i); + if (channelId.equals(jsonObject.get("channel_code"))) { + if ("1".equals(jsonObject.get("channel_type"))) { + url = jsonObject.get("assist_rtspurl").toString(); + } else { + url = jsonObject.get("channel_rtspurl").toString(); + } + + } + } + String app = "rtp"; + JSONObject list = zlmresTfulUtils.getMediaList(mediaServerItem, app, stream); + System.out.println("========================url===============================" + url); + if (list == null || ObjUtil.isEmpty(list.getJSONArray("data"))) { + JSONObject streamProxy = zlmresTfulUtils.addStreamProxy(mediaServerItem, app, stream, url, + false, false, "0"); +// System.out.println(streamProxy.toJSONString()); + } + String fmp4 = String.format("http://%s:%s/rtp/%s.live.mp4", mediaServerItem.getIp(), + mediaServerItem.getHttpPort(), + stream); + String ws_flv = String.format("ws://%s:%s/rtp/%s.live.flv", mediaServerItem.getIp(), + mediaServerItem.getHttpPort(), + stream); + StreamContent streamContent = new StreamContent(fmp4, ws_flv); + streamContent.setApp(app); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + wvpResult.setData(streamContent); + msg.setData(wvpResult); + result.setResult(wvpResult); + + // resultHolder.invokeAllResult(msg); + } + return result; + } + + @Override + public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + InviteTimeOutCallback timeoutCallback) { + + logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + // 超时处理 + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 + if (redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId) == null) { + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + logger.error("[点播超时], 发送BYE失败 {}", e.getMessage()); + } finally { + timeoutCallback.run(1, "收流超时"); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 取消订阅消息监听 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + } + } + }, userSetting.getPlayTimeout()); + //端口获取失败的ssrcInfo 没有必要发送点播指令 + if (ssrcInfo.getPort() <= 0) { + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); + dynamicTask.stop(timeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + RequestMessage msg = new RequestMessage(); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + device.getDeviceId() + channelId); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "点播端口分配异常")); + resultHolder.invokeAllResult(msg); + return; + } +// try { +// cmder.inviteVideo(device,channelId,mediaServerItem,ssrcInfo); +// } catch (InvalidArgumentException e) { +// e.printStackTrace(); +// } catch (SipException e) { +// e.printStackTrace(); +// } catch (ParseException e) { +// e.printStackTrace(); +// } + try { + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { + logger.info("收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(timeOutTaskKey); + + // hook响应 + onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId); + hookEvent.response(mediaServerItemInuse, response); + logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + + }, (event) -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + String contentString = new String(responseEvent.getResponse().getRawContent()); + // 获取ssrc + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + if (device.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { + String substring = contentString.substring(0, contentString.indexOf("y=")); + try { + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); + int port = -1; + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + for (Object description : mediaDescriptions) { + MediaDescription mediaDescription = (MediaDescription) description; + Media media = mediaDescription.getMedia(); + + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + break; + } + } + logger.info("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sdp.getConnection().getAddress(), port, device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + JSONObject jsonObject = zlmresTfulUtils.connectRtpServer(mediaServerItem, sdp.getConnection().getAddress(), port, ssrcInfo.getStream()); + logger.info("[点播-TCP主动连接对方] 结果: {}", jsonObject); + } catch (SdpException e) { + logger.error("[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", device.getDeviceId(), channelId, e); + } + } + return; + } + logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) { + // ssrc 不可用 + // 释放ssrc + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + event.msg = "下级自定义了ssrc,但是此ssrc不可用"; + event.statusCode = 400; + errorEvent.response(event); + return; + } + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(timeOutTaskKey); + // hook响应 + onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId); + hookEvent.response(mediaServerItemInUse, response); + }); + } + // 关闭rtp server + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{ + if (result) { + // 重新开启ssrc server + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort(), true, device.getStreamModeForParam()); + }else { + try { + logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + dynamicTask.stop(timeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + event.msg = "下级自定义了ssrc,重新设置收流信息失败"; + event.statusCode = 500; + errorEvent.response(event); + } + }); + + + } + } + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + errorEvent.response(event); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + + logger.error("[命令发送失败] 点播消息: {}", e.getMessage()); + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + } + + + + @Override + public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) { + StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); + RequestMessage msg = new RequestMessage(); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId); + if (streamInfo != null) { + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + redisCatchStorage.startPlay(streamInfo); + + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + wvpResult.setData(streamInfo); + + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + + } else { + logger.warn("设备预览API调用失败!"); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "设备预览API调用失败!")); + resultHolder.invokeAllResult(msg); + } + } + + @Override + public StreamInfo onPublishHandlerForPlay(MediaServerItem mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId) { + StreamInfo streamInfo = null; + Device device = redisCatchStorage.getDevice(deviceId); + streamInfo = onPublishHandler(mediaServerItem, mediaInfo, deviceId, channelId); + if (streamInfo != null) { + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId); + if (inviteInfo != null) { + inviteInfo.setStatus(InviteSessionStatus.ok); + inviteInfo.setStreamInfo(streamInfo); + inviteStreamService.updateInviteInfo(inviteInfo); + } + } + return streamInfo; + + } + + public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, MediaInfo mediaInfo, String deviceId, String channelId) { + StreamInfo streamInfo = mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", mediaInfo.getStream(), mediaInfo, null); + streamInfo.setDeviceID(deviceId); + streamInfo.setChannelId(channelId); + return streamInfo; + } + + private void onPublishHandlerForPlayback(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, PlayBackCallback playBackCallback) { + + StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); + PlayBackResult playBackResult = new PlayBackResult<>(); + if (streamInfo != null) { + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + redisCatchStorage.startPlay(streamInfo); + + + playBackResult.setCode(ErrorCode.SUCCESS.getCode()); + playBackResult.setMsg(ErrorCode.SUCCESS.getMsg()); + playBackResult.setData(streamInfo); + playBackCallback.call(playBackResult); + } else { + logger.warn("录像回放调用失败!"); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg("录像回放调用失败!"); + playBackCallback.call(playBackResult); + } + } + + @Override + public MediaServerItem getNewMediaServerItem(Device device) { + // if (device == null) { + // return null; + // } + MediaServerItem mediaServerItem; + if (device == null || ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); + } else { + mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); + } + if (mediaServerItem == null) { + logger.warn("点播时未找到可使用的ZLM..."); + } + return mediaServerItem; + } + + @Override + public MediaServerItem getNewMediaServerItemHasAssist(Device device) { + if (device == null) { + return null; + } + MediaServerItem mediaServerItem; + if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { + //mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(true); //郑顺利修改 at 20220617 + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(false); + } else { + mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); + } + if (mediaServerItem == null) { + logger.warn("[获取可用的ZLM节点]未找到可使用的ZLM..."); + } + return mediaServerItem; + } + + @Override + public void playBack(String deviceId, String channelId, String startTime, + String endTime, InviteStreamCallback inviteStreamCallback, + PlayBackCallback callback) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + MediaServerItem newMediaServerItem = getNewMediaServerItem(device); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback); + } + + @Override + public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, + String deviceId, String channelId, String startTime, + String endTime, InviteStreamCallback infoCallBack, + PlayBackCallback playBackCallback) { + if (mediaServerItem == null || ssrcInfo == null) { + return; + } + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在"); + } + logger.info("[回放消息] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + PlayBackResult playBackResult = new PlayBackResult<>(); + String playBackTimeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(playBackTimeOutTaskKey, () -> { + logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg("回放超时"); + + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[录像流]回放超时 发送BYE失败 {}", e.getMessage()); + } catch (SsrcTransactionNotFoundException e) { + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); + } + // 回复之前所有的点播请求 + playBackCallback.call(playBackResult); + }, userSetting.getPlayTimeout()); + + SipSubscribe.Event errorEvent = event -> { + dynamicTask.stop(playBackTimeOutTaskKey); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); + playBackResult.setEvent(event); + playBackCallback.call(playBackResult); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + }; + + InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> { + logger.info("收到回放订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); + dynamicTask.stop(playBackTimeOutTaskKey); + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); + if (streamInfo == null) { + logger.warn("设备回放API调用失败!"); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg("设备回放API调用失败!"); + playBackCallback.call(playBackResult); + return; + } + redisCatchStorage.startPlayback(streamInfo, inviteStreamInfo.getCallId()); + playBackResult.setCode(ErrorCode.SUCCESS.getCode()); + playBackResult.setMsg(ErrorCode.SUCCESS.getMsg()); + playBackResult.setData(streamInfo); + playBackResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem()); + playBackResult.setResponse(inviteStreamInfo.getResponse()); + playBackCallback.call(playBackResult); + }; + + try { + cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, + hookEvent, eventResult -> { + if (eventResult.type == SipSubscribe.EventResultType.response) { + ResponseEvent responseEvent = (ResponseEvent) eventResult.event; + String contentString = new String(responseEvent.getResponse().getRawContent()); + // 获取ssrc + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + return; + } + logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) { + // ssrc 不可用 + // 释放ssrc + dynamicTask.stop(playBackTimeOutTaskKey); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用"; + eventResult.statusCode = 400; + errorEvent.response(eventResult); + return; + } + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(playBackTimeOutTaskKey); + // hook响应 + onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, playBackCallback); + hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream())); + }); + } + + // 关闭rtp server + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{ + if (result) { + // 重新开启ssrc server + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), true, device.getStreamModeForParam()); + }else { + try { + logger.warn("[回放消息]停止 {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播 停止, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + dynamicTask.stop(playBackTimeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + errorEvent.response(eventResult); + eventResult.msg = "下级自定义了ssrc,重新设置收流信息失败"; + eventResult.statusCode = 500; + errorEvent.response(eventResult); + } + }); + } + } + } + + }, errorEvent); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 回放: {}", e.getMessage()); + + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + } + + + + @Override + public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device); + if (newMediaServerItem == null) { + PlayBackResult downloadResult = new PlayBackResult<>(); + downloadResult.setCode(ErrorCode.ERROR100.getCode()); + downloadResult.setMsg("未找到assist服务"); + playBackCallback.call(downloadResult); + return; + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, null, device.isSsrcCheck(), true, 0, false, device.getStreamModeForParam()); + download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, infoCallBack, playBackCallback); + } + + + @Override + public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) { + if (mediaServerItem == null || ssrcInfo == null) { + return; + } + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "不存在"); + } + PlayBackResult downloadResult = new PlayBackResult<>(); + logger.info("[录像下载] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + String downLoadTimeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> { + logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + downloadResult.setCode(ErrorCode.ERROR100.getCode()); + downloadResult.setMsg("录像下载请求超时"); + hookCallBack.call(downloadResult); + + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[录像流]录像下载请求超时, 发送BYE失败 {}", e.getMessage()); + } catch (SsrcTransactionNotFoundException e) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(deviceId, channelId, ssrcInfo.getStream()); + } + }, userSetting.getPlayTimeout()); + + SipSubscribe.Event errorEvent = event -> { + dynamicTask.stop(downLoadTimeOutTaskKey); + downloadResult.setCode(ErrorCode.ERROR100.getCode()); + downloadResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg)); + downloadResult.setEvent(event); + hookCallBack.call(downloadResult); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + }; + InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> { + logger.info("[录像下载]收到订阅消息: " + inviteStreamInfo.getCallId()); + dynamicTask.stop(downLoadTimeOutTaskKey); + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); + streamInfo.setStartTime(startTime); + streamInfo.setEndTime(endTime); + redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId()); + downloadResult.setCode(ErrorCode.SUCCESS.getCode()); + downloadResult.setMsg(ErrorCode.SUCCESS.getMsg()); + downloadResult.setData(streamInfo); + downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem()); + downloadResult.setResponse(inviteStreamInfo.getResponse()); + hookCallBack.call(downloadResult); + }; + try { + cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack, + hookEvent, errorEvent, eventResult -> + { + if (eventResult.type == SipSubscribe.EventResultType.response) { + ResponseEvent responseEvent = (ResponseEvent) eventResult.event; + String contentString = new String(responseEvent.getResponse().getRawContent()); + // 获取ssrc + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + return; + } + logger.info("[录像下载] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[录像下载] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + if (!ssrcFactory.checkSsrc(mediaServerItem.getId(),ssrcInResponse)) { + // ssrc 不可用 + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用"; + eventResult.statusCode = 400; + errorEvent.response(eventResult); + return; + } + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(downLoadTimeOutTaskKey); + // hook响应 + onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, hookCallBack); + hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream())); + }); + } + + // 关闭rtp server + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream(), result->{ + if (result) { + // 重新开启ssrc server + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort(), true, device.getStreamModeForParam()); + }else { + try { + logger.warn("[录像下载] 停止{}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 录像下载停止, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + dynamicTask.stop(downLoadTimeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + eventResult.msg = "下级自定义了ssrc,重新设置收流信息失败"; + eventResult.statusCode = 500; + errorEvent.response(eventResult); + } + }); + } + } + } + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 录像下载: {}", e.getMessage()); + + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + } + + @Override + public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) { + StreamInfo streamInfo = redisCatchStorage.queryDownload(deviceId, channelId, stream, null); + if (streamInfo != null) { + if (streamInfo.getProgress() == 1) { + return streamInfo; + } + + // 获取当前已下载时长 + String mediaServerId = streamInfo.getMediaServerId(); + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + logger.warn("查询录像信息时发现节点已离线"); + return null; + } + if (mediaServerItem.getRecordAssistPort() > 0) { + JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, streamInfo.getApp(), streamInfo.getStream(), null); + if (jsonObject == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败"); + } + if (jsonObject.getInteger("code") == 0) { + long duration = jsonObject.getLong("data"); + + if (duration == 0) { + streamInfo.setProgress(0); + } else { + String startTime = streamInfo.getStartTime(); + String endTime = streamInfo.getEndTime(); + long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime); + long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime); + + BigDecimal currentCount = new BigDecimal(duration / 1000); + BigDecimal totalCount = new BigDecimal(end - start); + BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP); + double process = divide.doubleValue(); + streamInfo.setProgress(process); + } + } + } + } + return streamInfo; + } + + @Override + public void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String uuid) { + RequestMessage msg = new RequestMessage(); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId); + msg.setId(uuid); + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); + if (streamInfo != null) { + redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId()); + msg.setData(JSON.toJSONString(streamInfo)); + resultHolder.invokeResult(msg); + } else { + logger.warn("设备预览API调用失败!"); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "设备预览API调用失败!")); + resultHolder.invokeResult(msg); + } + } + + + public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId) { + String streamId = resonse.getString("stream"); + JSONArray tracks = resonse.getJSONArray("tracks"); + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, null); + streamInfo.setDeviceID(deviceId); + streamInfo.setChannelId(channelId); + return streamInfo; + } + + @Override + public void zlmServerOffline(String mediaServerId) { + // 处理正在向上推流的上级平台 + List sendRtpItems = redisCatchStorage.querySendRTPServer(null); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + if (sendRtpItem.getMediaServerId().equals(mediaServerId)) { + ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + try { + sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + } + } + } + // 处理正在观看的国标设备 + List allSsrc = streamSession.getAllSsrc(); + if (allSsrc.size() > 0) { + for (SsrcTransaction ssrcTransaction : allSsrc) { + if (ssrcTransaction.getMediaServerId().equals(mediaServerId)) { + Device device = deviceService.getDevice(ssrcTransaction.getDeviceId()); + if (device == null) { + continue; + } + try { + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), + ssrcTransaction.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[zlm离线]为正在使用此zlm的设备, 发送BYE失败 {}", e.getMessage()); + } + } + } + } + } + + @Override + public void zlmServerOnline(String mediaServerId) { + // TODO 查找之前的点播,流如果不存在则给下级发送bye +// MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); +// zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ +// Integer code = mediaList.getInteger("code"); +// if (code == 0) { +// JSONArray data = mediaList.getJSONArray("data"); +// if (data == null || data.size() == 0) { +// zlmServerOffline(mediaServerId); +// }else { +// Map mediaListMap = new HashMap<>(); +// for (int i = 0; i < data.size(); i++) { +// JSONObject json = data.getJSONObject(i); +// String app = json.getString("app"); +// if ("rtp".equals(app)) { +// String stream = json.getString("stream"); +// if (mediaListMap.get(stream) != null) { +// continue; +// } +// mediaListMap.put(stream, json); +// // 处理正在观看的国标设备 +// List ssrcTransactions = streamSession.getSsrcTransactionForAll(null, null, null, stream); +// if (ssrcTransactions.size() > 0) { +// for (SsrcTransaction ssrcTransaction : ssrcTransactions) { +// if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) { +// cmder.streamByeCmd(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), +// ssrcTransaction.getStream(), null); +// } +// } +// } +// } +// } +// if (mediaListMap.size() > 0 ) { +// // 处理正在向上推流的上级平台 +// List sendRtpItems = redisCatchStorage.querySendRTPServer(null); +// if (sendRtpItems.size() > 0) { +// for (SendRtpItem sendRtpItem : sendRtpItems) { +// if (sendRtpItem.getMediaServerId().equals(mediaServerId)) { +// if (mediaListMap.get(sendRtpItem.getStreamId()) == null) { +// ParentPlatform platform = storager.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); +// sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); +// } +// } +// } +// } +// } +// } +// } +// })); + } + + @Override + public void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { + String key = redisCatchStorage.queryPlaybackForKey(null, null, streamId, null); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + logger.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + streamInfo.setPause(true); + redisTemplate.opsForValue().set(key, streamInfo); + MediaServerItem mediaServerItem = mediaServerService.getOne(streamInfo.getMediaServerId()); + if (null == mediaServerItem) { + logger.warn("mediaServer 不存在!"); + throw new ServiceException("mediaServer不存在"); + } + // zlm 暂停RTP超时检查 + JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamId); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + throw new ServiceException("暂停RTP接收失败"); + } + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); + cmder.playPauseCmd(device, streamInfo); + } + + @Override + public void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { + String key = redisCatchStorage.queryPlaybackForKey(null, null, streamId, null); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + logger.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + streamInfo.setPause(false); + redisTemplate.opsForValue().set(key, streamInfo); + MediaServerItem mediaServerItem = mediaServerService.getOne(streamInfo.getMediaServerId()); + if (null == mediaServerItem) { + logger.warn("mediaServer 不存在!"); + throw new ServiceException("mediaServer不存在"); + } + // zlm 暂停RTP超时检查 + JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamId); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + throw new ServiceException("继续RTP接收失败"); + } + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); + cmder.playResumeCmd(device, streamInfo); + } + + @Override + public AudioBroadcastResult audioBroadcast(Device device, String channelId, Boolean broadcastMode) { + // TODO 必须多端口模式才支持语音喊话鹤语音对讲 + if (device == null || channelId == null) { + return null; + } + logger.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channelId); + DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); + if (deviceChannel == null) { + logger.warn("开启语音广播的时候未找到通道: {}", channelId); + return null; + } + MediaServerItem mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null); + if (broadcastMode == null) { + broadcastMode = true; + } + String app = broadcastMode?"broadcast":"talk"; + String stream = device.getDeviceId() + "_" + channelId; + AudioBroadcastResult audioBroadcastResult = new AudioBroadcastResult(); + audioBroadcastResult.setApp(app); + audioBroadcastResult.setStream(stream); + audioBroadcastResult.setStreamInfo(new StreamContent(mediaServerService.getStreamInfoByAppAndStream(mediaServerItem, app, stream, null, null, null, false))); + //TODO G.711A测试 + audioBroadcastResult.setCodec("G.711"); + return audioBroadcastResult; + + } + + @Override + public void stopAudioBroadcast(String deviceId, String channelId) { + logger.info("[停止对讲] 设备:{}, 通道:{}", deviceId, channelId); + List audioBroadcastCatchList = new ArrayList<>(); + if (channelId == null) { + audioBroadcastCatchList.addAll(audioBroadcastManager.get(deviceId)); + } else { + audioBroadcastCatchList.add(audioBroadcastManager.get(deviceId, channelId)); + } + if (audioBroadcastCatchList.size() > 0) { + for (AudioBroadcastCatch audioBroadcastCatch : audioBroadcastCatchList) { + Device device = deviceService.getDevice(deviceId); + if (device == null || audioBroadcastCatch == null) { + return; + } + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(deviceId, audioBroadcastCatch.getChannelId(), null, null); + if (sendRtpItem != null) { + redisCatchStorage.deleteSendRTPServer(deviceId, sendRtpItem.getChannelId(), null, null); + MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStreamId(), null); + try { + cmder.streamByeCmdForDeviceInvite(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[消息发送失败] 发送语音喊话BYE失败"); + } + } + + audioBroadcastManager.del(deviceId, channelId); + } + } + } + + + @Override + public boolean audioBroadcastCmd(Device device, String channelId, MediaServerItem mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { + if (device == null || channelId == null) { + return false; + } + logger.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channelId); + DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); + if (deviceChannel == null) { + logger.warn("开启语音广播的时候未找到通道: {}", channelId); + event.call("开启语音广播的时候未找到通道"); + return false; + } + // 查询通道使用状态 + if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); + if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { + // 查询流是否存在,不存在则认为是异常状态 + Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, sendRtpItem.getApp(), sendRtpItem.getStreamId()); + if (streamReady) { + logger.warn("语音广播已经开启: {}", channelId); + event.call("语音广播已经开启"); + return false; + } else { + stopAudioBroadcast(device.getDeviceId(), channelId); + } + } + } + + // 发送通知 + cmder.audioBroadcastCmd(device, channelId, eventResultForOk -> { + // 发送成功 + AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channelId, mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform); + audioBroadcastManager.update(audioBroadcastCatch); + // 等待invite消息, 超时则结束 + String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId(); + if (!SipUtils.isFrontEnd(device.getDeviceId())) { + key += audioBroadcastCatch.getChannelId(); + } + dynamicTask.startDelay(key, ()->{ + logger.info("[语音广播]等待invite消息超时:{}/{}", device.getDeviceId(), channelId); + stopAudioBroadcast(device.getDeviceId(), channelId); + }, 10*1000); + }, eventResultForError -> { + // 发送失败 + logger.error("语音广播发送失败: {}:{}", channelId, eventResultForError.msg); + event.call("语音广播发送失败"); + stopAudioBroadcast(device.getDeviceId(), channelId); + }); + return true; + } + + @Override + public void talkCmd(Device device, String channelId, MediaServerItem mediaServerItem, String stream, AudioBroadcastEvent event) { + if (device == null || channelId == null) { + return; + } + // TODO 必须多端口模式才支持语音喊话鹤语音对讲 + logger.info("[语音对讲] device: {}, channel: {}", device.getDeviceId(), channelId); + DeviceChannel deviceChannel = storager.queryChannel(device.getDeviceId(), channelId); + if (deviceChannel == null) { + logger.warn("开启语音对讲的时候未找到通道: {}", channelId); + event.call("开启语音对讲的时候未找到通道"); + return; + } + // 查询通道使用状态 + if (audioBroadcastManager.exit(device.getDeviceId(), channelId)) { + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); + if (sendRtpItem != null && sendRtpItem.isOnlyAudio()) { + // 查询流是否存在,不存在则认为是异常状态 + MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + Boolean streamReady = mediaServerService.isStreamReady(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStreamId()); + if (streamReady) { + logger.warn("[语音对讲] 正在语音广播,无法开启语音通话: {}", channelId); + event.call("正在语音广播"); + return; + } else { + stopAudioBroadcast(device.getDeviceId(), channelId); + } + } + } + + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, stream, null); + if (sendRtpItem != null) { + MediaServerItem mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + Boolean streamReady = mediaServerService.isStreamReady(mediaServer, "rtp", sendRtpItem.getStreamId()); + if (streamReady) { + logger.warn("[语音对讲] 进行中: {}", channelId); + event.call("语音对讲进行中"); + return; + } else { + stopTalk(device, channelId); + } + } + + talk(mediaServerItem, device, channelId, stream, (hookData) -> { + logger.info("[语音对讲] 收到设备发来的流"); + }, eventResult -> { + logger.warn("[语音对讲] 失败,{}/{}, 错误码 {} {}", device.getDeviceId(), channelId, eventResult.statusCode, eventResult.msg); + event.call("失败,错误码 " + eventResult.statusCode + ", " + eventResult.msg); + }, () -> { + logger.warn("[语音对讲] 失败,{}/{} 超时", device.getDeviceId(), channelId); + event.call("失败,超时 "); + stopTalk(device, channelId); + }, errorMsg -> { + logger.warn("[语音对讲] 失败,{}/{} {}", device.getDeviceId(), channelId, errorMsg); + event.call(errorMsg); + stopTalk(device, channelId); + }); + } + + + private void stopTalk(Device device, String channelId) { + stopTalk(device, channelId, null); + } + + @Override + public void stopTalk(Device device, String channelId, Boolean streamIsReady) { + logger.info("[语音对讲] 停止, {}/{}", device.getDeviceId(), channelId); + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(device.getDeviceId(), channelId, null, null); + if (sendRtpItem == null) { + logger.info("[语音对讲] 停止失败, 未找到发送信息,可能已经停止"); + return; + } + // 停止向设备推流 + String mediaServerId = sendRtpItem.getMediaServerId(); + if (mediaServerId == null) { + return; + } + + MediaServerItem mediaServer = mediaServerService.getOne(mediaServerId); + + if (streamIsReady == null || streamIsReady) { + mediaServerService.stopSendRtp(mediaServer, sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getSsrc()); + } + + ssrcFactory.releaseSsrc(mediaServerId, sendRtpItem.getSsrc()); + + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, null, sendRtpItem.getStreamId()); + if (ssrcTransaction != null) { + try { + cmder.streamByeCmd(device, channelId, sendRtpItem.getStreamId(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + logger.info("[语音对讲] 停止消息发送失败,可能已经停止"); + } + } + redisCatchStorage.deleteSendRTPServer(device.getDeviceId(), channelId,null, null); + } + + private void talk(MediaServerItem mediaServerItem, Device device, String channelId, String stream, + HookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + Runnable timeoutCallback, AudioBroadcastEvent audioEvent) { + + String playSsrc = ssrcFactory.getPlaySsrc(mediaServerItem.getId()); + + if (playSsrc == null) { + audioEvent.call("ssrc已经用尽"); + return; + } + SendRtpItem sendRtpItem = new SendRtpItem(); + sendRtpItem.setApp("talk"); + sendRtpItem.setStreamId(stream); + sendRtpItem.setSsrc(playSsrc); + sendRtpItem.setDeviceId(device.getDeviceId()); + sendRtpItem.setPlatformId(device.getDeviceId()); + sendRtpItem.setChannelId(channelId); + sendRtpItem.setRtcp(false); + sendRtpItem.setMediaServerId(mediaServerItem.getId()); + sendRtpItem.setOnlyAudio(true); + sendRtpItem.setPlayType(InviteStreamType.TALK); + sendRtpItem.setPt(8); + sendRtpItem.setStatus(1); + sendRtpItem.setTcpActive(false); + sendRtpItem.setTcp(true); + sendRtpItem.setUsePs(false); +// sendRtpItem.setReceiveStream(stream + "_talk"); + + String callId = SipUtils.getNewCallId(); + int port = sendRtpPortManager.getNextPort(mediaServerItem); + //端口获取失败的ssrcInfo 没有必要发送点播指令 + if (port <= 0) { + logger.info("[语音对讲] 端口分配异常,deviceId={},channelId={}", device.getDeviceId(), channelId); + audioEvent.call("端口分配异常"); + return; + } + sendRtpItem.setLocalPort(port); + sendRtpItem.setPort(port); + logger.info("[语音对讲]开始 deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, sendRtpItem.getLocalPort(), device.getStreamMode(), sendRtpItem.getSsrc(), false); + // 超时处理 + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + + logger.info("[语音对讲] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, sendRtpItem.getPort(), sendRtpItem.getSsrc()); + timeoutCallback.run(); + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + try { + cmder.streamByeCmd(device, channelId, sendRtpItem.getStreamId(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + logger.error("[语音对讲]超时, 发送BYE失败 {}", e.getMessage()); + } finally { + timeoutCallback.run(); + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStreamId()); + } + }, userSetting.getPlayTimeout()); + + try { + mediaServerService.startSendRtpPassive(mediaServerItem, null, sendRtpItem, userSetting.getPlayTimeout() * 1000); + }catch (ControllerException e) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + logger.info("[语音对讲]失败 deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + audioEvent.call("失败, " + e.getMessage()); + // 查看是否已经建立了通道,存在则发送bye + stopTalk(device, channelId); + } + + + // 查看设备是否已经在推流 + try { + cmder.talkStreamCmd(mediaServerItem, sendRtpItem, device, channelId, callId, (hookData) -> { + logger.info("[语音对讲] 流已生成, 开始推流: " + hookData); + dynamicTask.stop(timeOutTaskKey); + // TODO 暂不做处理 + }, (hookData) -> { + logger.info("[语音对讲] 设备开始推流: " + hookData); + dynamicTask.stop(timeOutTaskKey); + + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + + if (event.event instanceof ResponseEvent) { + ResponseEvent responseEvent = (ResponseEvent) event.event; + if (responseEvent.getResponse() instanceof SIPResponse) { + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + sendRtpItem.setFromTag(response.getFromTag()); + sendRtpItem.setToTag(response.getToTag()); + sendRtpItem.setCallId(response.getCallIdHeader().getCallId()); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + streamSession.put(device.getDeviceId(), channelId, "talk", + sendRtpItem.getStreamId(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), + response, VideoStreamSessionManager.SessionType.TALK); + } else { + logger.error("[语音对讲]收到的消息错误,response不是SIPResponse"); + } + } else { + logger.error("[语音对讲]收到的消息错误,event不是ResponseEvent"); + } + + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStreamId()); + errorEvent.response(event); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + + logger.error("[命令发送失败] 对讲消息: {}", e.getMessage()); + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, sendRtpItem.getStreamId()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); + eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; + eventResult.statusCode = -1; + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + // } + + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/RecordInfoServerImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/RecordInfoServerImpl.java new file mode 100644 index 0000000..b1b9ea7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/RecordInfoServerImpl.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.service.IRecordInfoServer; +import com.yfd.monitor.storager.dao.RecordInfoDao; +import com.yfd.monitor.storager.dao.dto.RecordInfo; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RecordInfoServerImpl implements IRecordInfoServer { + + @Autowired + private RecordInfoDao recordInfoDao; + + @Override + public PageInfo getRecordList(int page, int count) { + PageHelper.startPage(page, count); + List all = recordInfoDao.selectAll(); + return new PageInfo<>(all); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/RoleServerImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/RoleServerImpl.java new file mode 100644 index 0000000..c057caf --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/RoleServerImpl.java @@ -0,0 +1,41 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.service.IRoleService; +import com.yfd.monitor.storager.dao.RoleMapper; +import com.yfd.monitor.storager.dao.dto.Role; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class RoleServerImpl implements IRoleService { + + @Autowired + private RoleMapper roleMapper; + + @Override + public Role getRoleById(int id) { + return roleMapper.selectById(id); + } + + @Override + public int add(Role role) { + return roleMapper.add(role); + } + + @Override + public int delete(int id) { + return roleMapper.delete(id); + } + + @Override + public List getAll() { + return roleMapper.selectAll(); + } + + @Override + public int update(Role role) { + return roleMapper.update(role); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamProxyServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamProxyServiceImpl.java new file mode 100644 index 0000000..e13c8f2 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamProxyServiceImpl.java @@ -0,0 +1,446 @@ +package com.yfd.monitor.service.impl; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IMediaService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.GbStreamMapper; +import com.yfd.monitor.storager.dao.ParentPlatformMapper; +import com.yfd.monitor.storager.dao.PlatformGbStreamMapper; +import com.yfd.monitor.storager.dao.StreamProxyMapper; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 视频代理业务 + */ +@Service +public class StreamProxyServiceImpl implements IStreamProxyService { + + private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class); + + @Autowired + private IVideoManagerStorage videoManagerStorager; + + @Autowired + private IMediaService mediaService; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private StreamProxyMapper streamProxyMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private ParentPlatformMapper parentPlatformMapper; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private IMediaServerService mediaServerService; + + @Resource + DataSourceTransactionManager dataSourceTransactionManager; + + @Resource + TransactionDefinition transactionDefinition; + + + @Override + public StreamInfo save(StreamProxyItem param) { + MediaServerItem mediaInfo; + if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){ + mediaInfo = mediaServerService.getMediaServerForMinimumLoad(null); + }else { + mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + } + if (mediaInfo == null) { + logger.warn("保存代理未找到在线的ZLM..."); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM"); + } + String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), + param.getStream() ); + param.setDst_url(dstUrl); + StringBuffer resultMsg = new StringBuffer(); + param.setMediaServerId(mediaInfo.getId()); + boolean saveResult; + // 更新 + if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { + saveResult = updateStreamProxy(param); + }else { // 新增 + saveResult = addStreamProxy(param); + } + if (!saveResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败"); + } + StreamInfo resultForStreamInfo = null; + resultMsg.append("保存成功"); + if (param.isEnable()) { + JSONObject jsonObject = addStreamProxyToZlm(param); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + resultMsg.append(", 但是启用失败,请检查流地址是否可用"); + param.setEnable(false); + // 直接移除 + if (param.isEnable_remove_none_reader()) { + del(param.getApp(), param.getStream()); + }else { + updateStreamProxy(param); + } + + }else { + resultForStreamInfo = mediaService.getStreamInfoByAppAndStream( + mediaInfo, param.getApp(), param.getStream(), null, null); + + } + } + return resultForStreamInfo; + } + + /** + * 新增代理流 + * @param streamProxyItem + * @return + */ + private boolean addStreamProxy(StreamProxyItem streamProxyItem) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + streamProxyItem.setStreamType("proxy"); + streamProxyItem.setStatus(true); + String now = DateUtil.getNow(); + streamProxyItem.setCreateTime(now); + try { + if (streamProxyMapper.add(streamProxyItem) > 0) { + if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { + if (gbStreamMapper.add(streamProxyItem) < 0) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + }else { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + result = true; + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }catch (Exception e) { + logger.error("向数据库添加流代理失败:", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + + + return result; + } + + /** + * 更新代理流 + * @param streamProxyItem + * @return + */ + @Override + public boolean updateStreamProxy(StreamProxyItem streamProxyItem) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + boolean result = false; + streamProxyItem.setStreamType("proxy"); + try { + if (streamProxyMapper.update(streamProxyItem) > 0) { + if (!ObjectUtils.isEmpty(streamProxyItem.getGbId())) { + if (gbStreamMapper.updateByAppAndStream(streamProxyItem) == 0) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + } else { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + result = true; + }catch (Exception e) { + logger.error("未处理的异常 ", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public JSONObject addStreamProxyToZlm(StreamProxyItem param) { + JSONObject result = null; + MediaServerItem mediaServerItem = null; + if (param.getMediaServerId() == null) { + logger.warn("添加代理时MediaServerId 为null"); + return null; + }else { + mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + } + if (mediaServerItem == null) { + return null; + } + if ("default".equals(param.getType())){ + result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(), + param.isEnable_audio(), param.isEnable_mp4(), param.getRtp_type()); + }else if ("ffmpeg".equals(param.getType())) { + result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrc_url(), param.getDst_url(), + param.getTimeout_ms() + "", param.isEnable_audio(), param.isEnable_mp4(), + param.getFfmpeg_cmd_key()); + + } + return result; + } + + @Override + public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) { + if (param ==null) { + return null; + } + MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream()); + return result; + } + + @Override + public PageInfo getAll(Integer page, Integer count) { + return videoManagerStorager.queryStreamProxyList(page, count); + } + + @Override + public void del(String app, String stream) { + StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxyItem != null) { + gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL); + videoManagerStorager.deleteStreamProxy(app, stream); + JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + // 如果关联了国标那么移除关联 + gbStreamMapper.del(app, stream); + platformGbStreamMapper.delByAppAndStream(app, stream); + // TODO 如果关联的推流, 那么状态设置为离线 + } + redisCatchStorage.removeStream(streamProxyItem.getMediaServerId(), "PULL", app, stream); + } + + + } + + @Override + public boolean start(String app, String stream) { + boolean result = false; + StreamProxyItem streamProxy = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxy != null && !streamProxy.isEnable() ) { + JSONObject jsonObject = addStreamProxyToZlm(streamProxy); + if (jsonObject == null) { + return false; + } + if (jsonObject.getInteger("code") == 0) { + result = true; + streamProxy.setEnable(true); + updateStreamProxy(streamProxy); + }else { + logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), + streamProxy.getSrc_url() == null? streamProxy.getUrl():streamProxy.getSrc_url()); + } + } + return result; + } + + @Override + public boolean stop(String app, String stream) { + boolean result = false; + StreamProxyItem streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream); + if (streamProxyDto != null && streamProxyDto.isEnable()) { + JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + streamProxyDto.setEnable(false); + result = updateStreamProxy(streamProxyDto); + } + } + return result; + } + + @Override + public JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem) { + JSONObject result = new JSONObject(); + JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0 + && mediaServerConfigResuly.getJSONArray("data").size() > 0){ + JSONObject mediaServerConfig = mediaServerConfigResuly.getJSONArray("data").getJSONObject(0); + + for (String key : mediaServerConfig.keySet()) { + if (key.startsWith("ffmpeg.cmd")){ + result.put(key, mediaServerConfig.getString(key)); + } + } + } + return result; + } + + + @Override + public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) { + return videoManagerStorager.getStreamProxyByAppAndStream(app, streamId); + } + + @Override + public void zlmServerOnline(String mediaServerId) { + // 移除开启了无人观看自动移除的流 + List streamProxyItemList = streamProxyMapper.selecAutoRemoveItemByMediaServerId(mediaServerId); + if (streamProxyItemList.size() > 0) { + gbStreamMapper.batchDel(streamProxyItemList); + } + streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); + + // 移除拉流代理生成的流信息 +// syncPullStream(mediaServerId); + + // 恢复流代理, 只查找这个这个流媒体 + List streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( + mediaServerId, true); + for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { + logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); + JSONObject jsonObject = addStreamProxyToZlm(streamProxyDto); + if (jsonObject == null) { + // 设置为离线 + logger.info("恢复流代理失败" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); + updateStatus(false, streamProxyDto.getApp(), streamProxyDto.getStream()); + }else { + updateStatus(true, streamProxyDto.getApp(), streamProxyDto.getStream()); + } + } + } + + @Override + public void zlmServerOffline(String mediaServerId) { + // 移除开启了无人观看自动移除的流 + List streamProxyItemList = streamProxyMapper.selecAutoRemoveItemByMediaServerId(mediaServerId); + if (streamProxyItemList.size() > 0) { + gbStreamMapper.batchDel(streamProxyItemList); + } + streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); + // 其他的流设置离线 + streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); + String type = "PULL"; + + // 发送redis消息 + List onStreamChangedHookParams = redisCatchStorage.getStreams(mediaServerId, type); + if (onStreamChangedHookParams.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : onStreamChangedHookParams) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", onStreamChangedHookParam.getApp()); + jsonObject.put("stream", onStreamChangedHookParam.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", mediaServerId); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + // 移除redis内流的信息 + redisCatchStorage.removeStream(mediaServerId, type, onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); + } + } + } + + @Override + public void clean() { + + } + + @Override + public int updateStatus(boolean status, String app, String stream) { + return streamProxyMapper.updateStatus(app, stream, status); + } + + private void syncPullStream(String mediaServerId){ + MediaServerItem mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer != null) { + List allPullStream = redisCatchStorage.getStreams(mediaServerId, "PULL"); + if (allPullStream.size() > 0) { + zlmresTfulUtils.getMediaList(mediaServer, jsonObject->{ + Map stringStreamInfoMap = new HashMap<>(); + if (jsonObject.getInteger("code") == 0) { + JSONArray data = jsonObject.getJSONArray("data"); + if(data != null && data.size() > 0) { + for (int i = 0; i < data.size(); i++) { + JSONObject streamJSONObj = data.getJSONObject(i); + if ("rtsp".equals(streamJSONObj.getString("schema"))) { + StreamInfo streamInfo = new StreamInfo(); + String app = streamJSONObj.getString("app"); + String stream = streamJSONObj.getString("stream"); + streamInfo.setApp(app); + streamInfo.setStream(stream); + stringStreamInfoMap.put(app+stream, streamInfo); + } + } + } + } + if (stringStreamInfoMap.size() == 0) { + redisCatchStorage.removeStream(mediaServerId, "PULL"); + }else { + for (String key : stringStreamInfoMap.keySet()) { + StreamInfo streamInfo = stringStreamInfoMap.get(key); + if (stringStreamInfoMap.get(streamInfo.getApp() + streamInfo.getStream()) == null) { + redisCatchStorage.removeStream(mediaServerId, "PULL", streamInfo.getApp(), + streamInfo.getStream()); + } + } + } + }); + } + + } + + } + + @Override + public ResourceBaceInfo getOverview() { + return streamProxyMapper.getOverview(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamPushServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamPushServiceImpl.java new file mode 100644 index 0000000..021da5d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamPushServiceImpl.java @@ -0,0 +1,537 @@ +package com.yfd.monitor.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yfd.monitor.conf.MediaConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.dto.*; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.media.zlm.dto.hook.OriginType; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.service.bean.StreamPushItemFromRedis; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.*; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class StreamPushServiceImpl implements IStreamPushService { + + private final static Logger logger = LoggerFactory.getLogger(StreamPushServiceImpl.class); + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private StreamProxyMapper streamProxyMapper; + + @Autowired + private ParentPlatformMapper parentPlatformMapper; + + @Autowired + private PlatformCatalogMapper platformCatalogMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private EventPublisher eventPublisher; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + private MediaConfig mediaConfig; + + + @Override + public List handleJSON(String jsonData, MediaServerItem mediaServerItem) { + if (jsonData == null) { + return null; + } + + Map result = new HashMap<>(); + + List onStreamChangedHookParams = JSON.parseObject(jsonData, new TypeReference>() {}); + for (OnStreamChangedHookParam item : onStreamChangedHookParams) { + + // 不保存国标推理以及拉流代理的流 + if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || item.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) { + String key = item.getApp() + "_" + item.getStream(); + StreamPushItem streamPushItem = result.get(key); + if (streamPushItem == null) { + streamPushItem = transform(item); + result.put(key, streamPushItem); + } + } + } + + return new ArrayList<>(result.values()); + } + @Override + public StreamPushItem transform(OnStreamChangedHookParam item) { + StreamPushItem streamPushItem = new StreamPushItem(); + streamPushItem.setApp(item.getApp()); + streamPushItem.setMediaServerId(item.getMediaServerId()); + streamPushItem.setStream(item.getStream()); + streamPushItem.setAliveSecond(item.getAliveSecond()); + streamPushItem.setOriginSock(item.getOriginSock()); + streamPushItem.setTotalReaderCount(item.getTotalReaderCount()); + streamPushItem.setOriginType(item.getOriginType()); + streamPushItem.setOriginTypeStr(item.getOriginTypeStr()); + streamPushItem.setOriginUrl(item.getOriginUrl()); + streamPushItem.setCreateTime(DateUtil.getNow()); + streamPushItem.setAliveSecond(item.getAliveSecond()); + streamPushItem.setStatus(true); + streamPushItem.setStreamType("push"); + streamPushItem.setVhost(item.getVhost()); + streamPushItem.setServerId(item.getSeverId()); + return streamPushItem; + } + + @Override + public PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) { + PageHelper.startPage(page, count); + List all = streamPushMapper.selectAllForList(query, pushing, mediaServerId); + return new PageInfo<>(all); + } + + @Override + public List getPushList(String mediaServerId) { + return streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); + } + + @Override + public boolean saveToGB(GbStream stream) { + stream.setStreamType("push"); + stream.setStatus(true); + stream.setCreateTime(DateUtil.getNow()); + stream.setStreamType("push"); + stream.setMediaServerId(mediaConfig.getId()); + int add = gbStreamMapper.add(stream); + return add > 0; + } + + @Override + public boolean removeFromGB(GbStream stream) { + // 判断是否需要发送事件 + gbStreamService.sendCatalogMsg(stream, CatalogEvent.DEL); + platformGbStreamMapper.delByAppAndStream(stream.getApp(), stream.getStream()); + int del = gbStreamMapper.del(stream.getApp(), stream.getStream()); + MediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId()); + JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, stream.getApp(), stream.getStream()); + if (mediaList != null) { + if (mediaList.getInteger("code") == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data == null) { + streamPushMapper.del(stream.getApp(), stream.getStream()); + } + } + } + return del > 0; + } + + + @Override + public StreamPushItem getPush(String app, String streamId) { + return streamPushMapper.selectOne(app, streamId); + } + + @Override + public boolean stop(String app, String streamId) { + StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId); + if (streamPushItem != null) { + gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL); + } + + platformGbStreamMapper.delByAppAndStream(app, streamId); + gbStreamMapper.del(app, streamId); + int delStream = streamPushMapper.del(app, streamId); + if (delStream > 0) { + MediaServerItem mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); + zlmresTfulUtils.closeStreams(mediaServerItem,app, streamId); + } + return true; + } + + @Override + public void zlmServerOnline(String mediaServerId) { + // 同步zlm推流信息 + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + return; + } + // 数据库记录 + List pushList = getPushList(mediaServerId); + Map pushItemMap = new HashMap<>(); + // redis记录 + List onStreamChangedHookParams = redisCatchStorage.getStreams(mediaServerId, "PUSH"); + Map streamInfoPushItemMap = new HashMap<>(); + if (pushList.size() > 0) { + for (StreamPushItem streamPushItem : pushList) { + if (ObjectUtils.isEmpty(streamPushItem.getGbId())) { + pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + } + } + } + if (onStreamChangedHookParams.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : onStreamChangedHookParams) { + streamInfoPushItemMap.put(onStreamChangedHookParam.getApp() + onStreamChangedHookParam.getStream(), onStreamChangedHookParam); + } + } + // 获取所有推流鉴权信息,清理过期的 + List allStreamAuthorityInfo = redisCatchStorage.getAllStreamAuthorityInfo(); + Map streamAuthorityInfoInfoMap = new HashMap<>(); + for (StreamAuthorityInfo streamAuthorityInfo : allStreamAuthorityInfo) { + streamAuthorityInfoInfoMap.put(streamAuthorityInfo.getApp() + streamAuthorityInfo.getStream(), streamAuthorityInfo); + } + zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ + if (mediaList == null) { + return; + } + String dataStr = mediaList.getString("data"); + + Integer code = mediaList.getInteger("code"); + List streamPushItems = null; + if (code == 0 ) { + if (dataStr != null) { + streamPushItems = handleJSON(dataStr, mediaServerItem); + } + } + + if (streamPushItems != null) { + for (StreamPushItem streamPushItem : streamPushItems) { + pushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + streamInfoPushItemMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + streamAuthorityInfoInfoMap.remove(streamPushItem.getApp() + streamPushItem.getStream()); + } + } + List offlinePushItems = new ArrayList<>(pushItemMap.values()); + if (offlinePushItems.size() > 0) { + String type = "PUSH"; + int runLimit = 300; + if (offlinePushItems.size() > runLimit) { + for (int i = 0; i < offlinePushItems.size(); i += runLimit) { + int toIndex = i + runLimit; + if (i + runLimit > offlinePushItems.size()) { + toIndex = offlinePushItems.size(); + } + List streamPushItemsSub = offlinePushItems.subList(i, toIndex); + streamPushMapper.delAll(streamPushItemsSub); + } + }else { + streamPushMapper.delAll(offlinePushItems); + } + + } + Collection offlineOnStreamChangedHookParamList = streamInfoPushItemMap.values(); + if (offlineOnStreamChangedHookParamList.size() > 0) { + String type = "PUSH"; + for (OnStreamChangedHookParam offlineOnStreamChangedHookParam : offlineOnStreamChangedHookParamList) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", offlineOnStreamChangedHookParam.getApp()); + jsonObject.put("stream", offlineOnStreamChangedHookParam.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", mediaServerId); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + // 移除redis内流的信息 + redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream()); + } + } + + Collection streamAuthorityInfos = streamAuthorityInfoInfoMap.values(); + if (streamAuthorityInfos.size() > 0) { + for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) { + // 移除redis内流的信息 + redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream()); + } + } + })); + } + + @Override + public void zlmServerOffline(String mediaServerId) { + List streamPushItems = streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); + // 移除没有GBId的推流 + streamPushMapper.deleteWithoutGBId(mediaServerId); + gbStreamMapper.deleteWithoutGBId("push", mediaServerId); + // 其他的流设置未启用 + streamPushMapper.updateStatusByMediaServerId(mediaServerId, false); + streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); + // 发送流停止消息 + String type = "PUSH"; + // 发送redis消息 + List streamInfoList = redisCatchStorage.getStreams(mediaServerId, type); + if (streamInfoList.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : streamInfoList) { + // 移除redis内流的信息 + redisCatchStorage.removeStream(mediaServerId, type, onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", onStreamChangedHookParam.getApp()); + jsonObject.put("stream", onStreamChangedHookParam.getStream()); + jsonObject.put("register", false); + jsonObject.put("mediaServerId", mediaServerId); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + } + } + } + + @Override + public void clean() { + + } + + @Override + public boolean saveToRandomGB() { + List streamPushItems = streamPushMapper.selectAll(); + long gbId = 100001; + for (StreamPushItem streamPushItem : streamPushItems) { + streamPushItem.setStreamType("push"); + streamPushItem.setStatus(true); + streamPushItem.setGbId("34020000004111" + gbId); + streamPushItem.setCreateTime(DateUtil.getNow()); + gbId ++; + } + int limitCount = 30; + + if (streamPushItems.size() > limitCount) { + for (int i = 0; i < streamPushItems.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > streamPushItems.size()) { + toIndex = streamPushItems.size(); + } + gbStreamMapper.batchAdd(streamPushItems.subList(i, toIndex)); + } + }else { + gbStreamMapper.batchAdd(streamPushItems); + } + return true; + } + + @Override + public void batchAdd(List streamPushItems) { + streamPushMapper.addAll(streamPushItems); + gbStreamMapper.batchAdd(streamPushItems); + } + + + @Override + public void batchAddForUpload(List streamPushItems, Map> streamPushItemsForAll ) { + // 存储数据到stream_push表 + streamPushMapper.addAll(streamPushItems); + List streamPushItemForGbStream = streamPushItems.stream() + .filter(streamPushItem-> streamPushItem.getGbId() != null) + .collect(Collectors.toList()); + // 存储数据到gb_stream表, id会返回到streamPushItemForGbStream里 + if (streamPushItemForGbStream.size() > 0) { + gbStreamMapper.batchAdd(streamPushItemForGbStream); + } + // 去除没有ID也就是没有存储到数据库的数据 + List streamPushItemsForPlatform = streamPushItemForGbStream.stream() + .filter(streamPushItem-> streamPushItem.getGbStreamId() != null) + .collect(Collectors.toList()); + + if (streamPushItemsForPlatform.size() > 0) { + // 获取所有平台,平台和目录信息一般不会特别大量。 + List parentPlatformList = parentPlatformMapper.getParentPlatformList(); + Map> platformInfoMap = new HashMap<>(); + if (parentPlatformList.size() == 0) { + return; + } + for (ParentPlatform platform : parentPlatformList) { + Map catalogMap = new HashMap<>(); + + // 创建根节点 + PlatformCatalog platformCatalog = new PlatformCatalog(); + platformCatalog.setId(platform.getServerGBId()); + catalogMap.put(platform.getServerGBId(), platformCatalog); + + // 查询所有节点信息 + List platformCatalogs = platformCatalogMapper.selectByPlatForm(platform.getServerGBId()); + if (platformCatalogs.size() > 0) { + for (PlatformCatalog catalog : platformCatalogs) { + catalogMap.put(catalog.getId(), catalog); + } + } + platformInfoMap.put(platform.getServerGBId(), catalogMap); + } + List streamPushItemListFroPlatform = new ArrayList<>(); + Map> platformForEvent = new HashMap<>(); + // 遍历存储结果,查找app+Stream->platformId+catalogId的对应关系,然后执行批量写入 + for (StreamPushItem streamPushItem : streamPushItemsForPlatform) { + List platFormInfoList = streamPushItemsForAll.get(streamPushItem.getApp() + streamPushItem.getStream()); + if (platFormInfoList != null && platFormInfoList.size() > 0) { + for (String[] platFormInfoArray : platFormInfoList) { + StreamPushItem streamPushItemForPlatform = new StreamPushItem(); + streamPushItemForPlatform.setGbStreamId(streamPushItem.getGbStreamId()); + if (platFormInfoArray.length > 0) { + // 数组 platFormInfoArray 0 为平台ID。 1为目录ID + // 不存在这个平台,则忽略导入此关联关系 + if (platformInfoMap.get(platFormInfoArray[0]) == null + || platformInfoMap.get(platFormInfoArray[0]).get(platFormInfoArray[1]) == null) { + logger.info("导入数据时不存在平台或目录{}/{},已导入未分配", platFormInfoArray[0], platFormInfoArray[1] ); + continue; + } + streamPushItemForPlatform.setPlatformId(platFormInfoArray[0]); + List gbStreamList = platformForEvent.get(platFormInfoArray[0]); + if (gbStreamList == null) { + gbStreamList = new ArrayList<>(); + platformForEvent.put(platFormInfoArray[0], gbStreamList); + } + // 为发送通知整理数据 + streamPushItemForPlatform.setName(streamPushItem.getName()); + streamPushItemForPlatform.setApp(streamPushItem.getApp()); + streamPushItemForPlatform.setStream(streamPushItem.getStream()); + streamPushItemForPlatform.setGbId(streamPushItem.getGbId()); + gbStreamList.add(streamPushItemForPlatform); + } + if (platFormInfoArray.length > 1) { + streamPushItemForPlatform.setCatalogId(platFormInfoArray[1]); + } + streamPushItemListFroPlatform.add(streamPushItemForPlatform); + } + + } + } + if (streamPushItemListFroPlatform.size() > 0) { + platformGbStreamMapper.batchAdd(streamPushItemListFroPlatform); + // 发送通知 + for (String platformId : platformForEvent.keySet()) { + eventPublisher.catalogEventPublishForStream( + platformId, platformForEvent.get(platformId), CatalogEvent.ADD); + } + } + } + } + + @Override + public boolean batchStop(List gbStreams) { + if (gbStreams == null || gbStreams.size() == 0) { + return false; + } + gbStreamService.sendCatalogMsgs(gbStreams, CatalogEvent.DEL); + + platformGbStreamMapper.delByGbStreams(gbStreams); + gbStreamMapper.batchDelForGbStream(gbStreams); + int delStream = streamPushMapper.delAllForGbStream(gbStreams); + if (delStream > 0) { + for (GbStream gbStream : gbStreams) { + MediaServerItem mediaServerItem = mediaServerService.getOne(gbStream.getMediaServerId()); + zlmresTfulUtils.closeStreams(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + } + + } + return true; + } + + @Override + public void allStreamOffline() { + List onlinePushers = streamPushMapper.getOnlinePusherForGb(); + if (onlinePushers.size() == 0) { + return; + } + streamPushMapper.setAllStreamOffline(); + + // 发送通知 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF); + } + + @Override + public void offline(List offlineStreams) { + // 更新部分设备离线 + List onlinePushers = streamPushMapper.getOnlinePusherForGbInList(offlineStreams); + streamPushMapper.offline(offlineStreams); + // 发送通知 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.OFF); + } + + @Override + public void online(List onlineStreams) { + // 更新部分设备上线streamPushService + List onlinePushers = streamPushMapper.getOfflinePusherForGbInList(onlineStreams); + streamPushMapper.online(onlineStreams); + // 发送通知 + eventPublisher.catalogEventPublishForStream(null, onlinePushers, CatalogEvent.ON); + } + + @Override + public boolean add(StreamPushItem stream) { + stream.setUpdateTime(DateUtil.getNow()); + stream.setCreateTime(DateUtil.getNow()); + stream.setServerId(userSetting.getServerId()); + + // 放在事务内执行 + boolean result = false; + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + try { + int addStreamResult = streamPushMapper.add(stream); + if (!ObjectUtils.isEmpty(stream.getGbId())) { + stream.setStreamType("push"); + gbStreamMapper.add(stream); + } + dataSourceTransactionManager.commit(transactionStatus); + result = true; + }catch (Exception e) { + logger.error("批量移除流与平台的关系时错误", e); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; + } + + @Override + public List getAllAppAndStream() { + + return streamPushMapper.getAllAppAndStream(); + } + + @Override + public ResourceBaceInfo getOverview() { + return streamPushMapper.getOverview(userSetting.isUsePushingAsStatus()); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamPushUploadFileHandler.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamPushUploadFileHandler.java new file mode 100644 index 0000000..b3e4678 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/StreamPushUploadFileHandler.java @@ -0,0 +1,176 @@ +package com.yfd.monitor.service.impl; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.StreamPushExcelDto; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.springframework.util.ObjectUtils; + +import java.util.*; + +public class StreamPushUploadFileHandler extends AnalysisEventListener { + + /** + * 错误数据的回调,用于将错误数据发送给页面 + */ + private ErrorDataHandler errorDataHandler; + + /** + * 推流的业务类用于存储数据 + */ + private IStreamPushService pushService; + + /** + * 默认流媒体节点ID + */ + private String defaultMediaServerId; + + /** + * 用于存储不加过滤的所有数据 + */ + private List streamPushItems = new ArrayList<>(); + + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ + private Map streamPushItemForSave = new HashMap<>(); + + /** + * 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表 + */ + private Map> streamPushItemsForPlatform = new HashMap<>(); + + /** + * 用于判断文件是否存在重复的app+Stream+平台ID + */ + private Set streamPushStreamSet = new HashSet<>(); + + /** + * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 + */ + private BiMap gBMap = HashBiMap.create(); + + /** + * 记录错误的APP+Stream + */ + private List errorStreamList = new ArrayList<>(); + + + /** + * 记录错误的国标ID + */ + private List errorGBList = new ArrayList<>(); + + /** + * 读取数量计数器 + */ + private int loadedSize = 0; + + public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { + this.pushService = pushService; + this.defaultMediaServerId = defaultMediaServerId; + this.errorDataHandler = errorDataHandler; + } + + public interface ErrorDataHandler{ + void handle(List streams, List gbId); + } + + @Override + public void invoke(StreamPushExcelDto streamPushExcelDto, AnalysisContext analysisContext) { + if (ObjectUtils.isEmpty(streamPushExcelDto.getApp()) + || ObjectUtils.isEmpty(streamPushExcelDto.getStream()) + || ObjectUtils.isEmpty(streamPushExcelDto.getGbId())) { + return; + } + + if (gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()) == null) { + try { + gBMap.put(streamPushExcelDto.getApp() + streamPushExcelDto.getStream(), streamPushExcelDto.getGbId()); + }catch (IllegalArgumentException e) { + errorGBList.add(streamPushExcelDto.getGbId() + "(不同的app+stream使用了相同的国标ID)"); + return; + } + }else { + if (!gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()).equals(streamPushExcelDto.getGbId())) { + errorGBList.add(streamPushExcelDto.getGbId() + "(同一组app+stream使用了不同的国标ID)"); + return; + } + } + + if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId())) { + errorStreamList.add(streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()+ "/" + + streamPushExcelDto.getPlatformId() + "(同一组app+stream添加在了同一个平台下)"); + return; + }else { + streamPushStreamSet.add(streamPushExcelDto.getApp()+streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId()); + } + + StreamPushItem streamPushItem = new StreamPushItem(); + streamPushItem.setApp(streamPushExcelDto.getApp()); + streamPushItem.setStream(streamPushExcelDto.getStream()); + streamPushItem.setGbId(streamPushExcelDto.getGbId()); + streamPushItem.setStatus(streamPushExcelDto.getStatus()); + streamPushItem.setStreamType("push"); + streamPushItem.setCreateTime(DateUtil.getNow()); + streamPushItem.setMediaServerId(defaultMediaServerId); + streamPushItem.setName(streamPushExcelDto.getName()); + streamPushItem.setOriginType(2); + streamPushItem.setOriginTypeStr("rtsp_push"); + streamPushItem.setTotalReaderCount("0"); + streamPushItem.setPlatformId(streamPushExcelDto.getPlatformId()); + streamPushItem.setCatalogId(streamPushExcelDto.getCatalogId()); + + // 存入所有的通道信息 + streamPushItems.add(streamPushItem); + streamPushItemForSave.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + + if (!ObjectUtils.isEmpty(streamPushExcelDto.getPlatformId())) { + List platformList = streamPushItemsForPlatform.get(streamPushItem.getApp() + streamPushItem.getStream()); + if (platformList == null) { + platformList = new ArrayList<>(); + streamPushItemsForPlatform.put(streamPushItem.getApp() + streamPushItem.getStream(), platformList); + } + String platformId = streamPushExcelDto.getPlatformId(); + String catalogId = streamPushExcelDto.getCatalogId(); + if (ObjectUtils.isEmpty(streamPushExcelDto.getCatalogId())) { + catalogId = null; + } + String[] platFormInfoArray = new String[]{platformId, catalogId}; + platformList.add(platFormInfoArray); + } + + loadedSize ++; + if (loadedSize > 1000) { + saveData(); + streamPushItems.clear(); + streamPushItemForSave.clear(); + streamPushItemsForPlatform.clear(); + loadedSize = 0; + } + + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + // 这里也要保存数据,确保最后遗留的数据也存储到数据库 + saveData(); + streamPushItems.clear(); + streamPushItemForSave.clear(); + gBMap.clear(); + streamPushStreamSet.clear(); + streamPushItemsForPlatform.clear(); + errorDataHandler.handle(errorStreamList, errorGBList); + } + + private void saveData(){ + if (streamPushItemForSave.size() > 0) { + // 向数据库查询是否存在重复的app + pushService.batchAddForUpload(new ArrayList<>(streamPushItemForSave.values()), streamPushItemsForPlatform); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/impl/UserServiceImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..864cb65 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/impl/UserServiceImpl.java @@ -0,0 +1,81 @@ +package com.yfd.monitor.service.impl; + +import com.yfd.monitor.service.IUserService; +import com.yfd.monitor.storager.dao.UserMapper; +import com.yfd.monitor.storager.dao.dto.User; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.util.List; + +@Service +public class UserServiceImpl implements IUserService { + + @Autowired + private UserMapper userMapper; + + @Override + public User getUser(String username, String password) { + return userMapper.select(username, password); + } + + @Override + public boolean changePassword(int id, String password) { + User user = userMapper.selectById(id); + user.setPassword(password); + return userMapper.update(user) > 0; + } + + @Override + public User getUserByUsername(String username) { + return userMapper.getUserByUsername(username); + } + + @Override + public int addUser(User user) { + User userByUsername = userMapper.getUserByUsername(user.getUsername()); + if (userByUsername != null) { + return 0; + } + return userMapper.add(user); + } + @Override + public int deleteUser(int id) { + return userMapper.delete(id); + } + + @Override + public List getAllUsers() { + return userMapper.selectAll(); + } + + @Override + public int updateUsers(User user) { + return userMapper.update(user); + } + + + @Override + public boolean checkPushAuthority(String callId, String sign) { + if (ObjectUtils.isEmpty(callId)) { + return userMapper.checkPushAuthorityByCallId(sign).size() > 0; + }else { + return userMapper.checkPushAuthorityByCallIdAndSign(callId, sign).size() > 0; + } + } + + @Override + public PageInfo getUsers(int page, int count) { + PageHelper.startPage(page, count); + List users = userMapper.getUsers(); + return new PageInfo<>(users); + } + + @Override + public int changePushKey(int id, String pushKey) { + return userMapper.changePushKey(id,pushKey); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisAlarmMsgListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisAlarmMsgListener.java new file mode 100644 index 0000000..bfd6eff --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisAlarmMsgListener.java @@ -0,0 +1,151 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.validation.constraints.NotNull; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + + +@Component +public class RedisAlarmMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisAlarmMsgListener.class); + + @Autowired + private ISIPCommander commander; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private IVideoManagerStorage storage; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Autowired + private UserSetting userSetting; + + @Override + public void onMessage(@NotNull Message message, byte[] bytes) { + // 消息示例: PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }' + logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { +// logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize()); + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + AlarmChannelMessage alarmChannelMessage = JSON.parseObject(msg.getBody(), AlarmChannelMessage.class); + if (alarmChannelMessage == null) { + logger.warn("[REDIS的ALARM通知]消息解析失败"); + continue; + } + String gbId = alarmChannelMessage.getGbId(); + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setChannelId(gbId); + deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); + deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); + deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType()); + deviceAlarm.setAlarmPriority("1"); + deviceAlarm.setAlarmTime(DateUtil.getNow()); + deviceAlarm.setLongitude(0); + deviceAlarm.setLatitude(0); + + if (ObjectUtils.isEmpty(gbId)) { + if (userSetting.getSendToPlatformsWhenIdLost()) { + // 发送给所有的上级 + List parentPlatforms = storage.queryEnableParentPlatformList(true); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + try { + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); + commanderForPlatform.sendAlarmMessage(parentPlatform, new ArrayList<>()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + } + } + } + }else { + // 获取开启了消息推送的设备和平台 + List parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel(); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + try { + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); + commanderForPlatform.sendAlarmMessage(parentPlatform, new ArrayList<>()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + } + } + } + + } + // 获取开启了消息推送的设备和平台 + List devices = storage.queryDeviceWithAsMessageChannel(); + if (devices.size() > 0) { + for (Device device : devices) { + try { + deviceAlarm.setChannelId(device.getDeviceId()); + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + } + } + + }else { + Device device = storage.queryVideoDevice(gbId); + ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId); + if (device != null && platform == null) { + try { + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + }else if (device == null && platform != null){ + try { + commanderForPlatform.sendAlarmMessage(platform, new ArrayList<>()); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + }else { + logger.warn("无法确定" + gbId + "是平台还是设备"); + } + } + }catch (Exception e) { + logger.error("未处理的异常 ", e); + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisGbPlayMsgListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisGbPlayMsgListener.java new file mode 100644 index 0000000..7a7abf5 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisGbPlayMsgListener.java @@ -0,0 +1,393 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.SendRtpItem; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.HookSubscribeFactory; +import com.yfd.monitor.media.zlm.dto.HookSubscribeForStreamChange; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.bean.*; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 监听下级发送推送信息,并发送国标推流消息上级 + */ +@Component +public class RedisGbPlayMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class); + + public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM"; + + /** + * 流媒体不存在的错误玛 + */ + public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1; + + /** + * 离线的错误玛 + */ + public static final int ERROR_CODE_OFFLINE = -2; + + /** + * 超时的错误玛 + */ + public static final int ERROR_CODE_TIMEOUT = -3; + + private Map callbacks = new ConcurrentHashMap<>(); + private Map callbacksForStartSendRtpStream = new ConcurrentHashMap<>(); + private Map callbacksForError = new ConcurrentHashMap<>(); + + @Autowired + private UserSetting userSetting; + + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + + @Autowired + private DynamicTask dynamicTask; + + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + public interface PlayMsgCallback{ + void handler(ResponseSendItemMsg responseSendItemMsg) throws ParseException; + } + + public interface PlayMsgCallbackForStartSendRtpStream{ + void handler(JSONObject jsonObject); + } + + public interface PlayMsgErrorCallback{ + void handler(WVPResult wvpResult); + } + + @Override + public void onMessage(Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + JSONObject msgJSON = JSON.parseObject(msg.getBody(), JSONObject.class); + WvpRedisMsg wvpRedisMsg = JSON.to(WvpRedisMsg.class, msgJSON); + if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) { + continue; + } + if (WvpRedisMsg.isRequest(wvpRedisMsg)) { + logger.info("[收到REDIS通知] 请求: {}", new String(msg.getBody())); + + switch (wvpRedisMsg.getCmd()){ + case WvpRedisMsgCmd.GET_SEND_ITEM: + RequestSendItemMsg content = JSON.to(RequestSendItemMsg.class, wvpRedisMsg.getContent()); + requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); + break; + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: + RequestPushStreamMsg param = JSON.to(RequestPushStreamMsg.class, wvpRedisMsg.getContent()); + requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); + break; + default: + break; + } + + }else { + logger.info("[收到REDIS通知] 回复: {}", new String(msg.getBody())); + switch (wvpRedisMsg.getCmd()){ + case WvpRedisMsgCmd.GET_SEND_ITEM: + + WVPResult content = JSON.to(WVPResult.class, wvpRedisMsg.getContent()); + + String key = wvpRedisMsg.getSerial(); + switch (content.getCode()) { + case 0: + ResponseSendItemMsg responseSendItemMsg =JSON.to(ResponseSendItemMsg.class, content.getData()); + PlayMsgCallback playMsgCallback = callbacks.get(key); + if (playMsgCallback != null) { + callbacksForError.remove(key); + try { + playMsgCallback.handler(responseSendItemMsg); + } catch (ParseException e) { + logger.error("[REDIS消息处理异常] ", e); + } + } + break; + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND: + case ERROR_CODE_OFFLINE: + case ERROR_CODE_TIMEOUT: + PlayMsgErrorCallback errorCallback = callbacksForError.get(key); + if (errorCallback != null) { + callbacks.remove(key); + errorCallback.handler(content); + } + break; + default: + break; + } + break; + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: + WVPResult wvpResult = JSON.to(WVPResult.class, wvpRedisMsg.getContent()); + String serial = wvpRedisMsg.getSerial(); + switch (wvpResult.getCode()) { + case 0: + JSONObject jsonObject = (JSONObject)wvpResult.getData(); + PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial); + if (playMsgCallback != null) { + callbacksForError.remove(serial); + playMsgCallback.handler(jsonObject); + } + break; + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND: + case ERROR_CODE_OFFLINE: + case ERROR_CODE_TIMEOUT: + PlayMsgErrorCallback errorCallback = callbacksForError.get(serial); + if (errorCallback != null) { + callbacks.remove(serial); + errorCallback.handler(wvpResult); + } + break; + default: + break; + } + break; + default: + break; + } + + } + }catch (Exception e) { + logger.warn("[RedisGbPlayMsg] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[RedisGbPlayMsg] 异常内容: ", e); + } + } + }); + } + } + + /** + * 处理收到的请求推流的请求 + */ + private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) { + MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId()); + if (mediaInfo == null) { + // TODO 回复错误 + return; + } + String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1"; + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",requestPushStreamMsg.getApp()); + param.put("stream",requestPushStreamMsg.getStream()); + param.put("ssrc", requestPushStreamMsg.getSsrc()); + param.put("dst_url",requestPushStreamMsg.getIp()); + param.put("dst_port", requestPushStreamMsg.getPort()); + param.put("is_udp", is_Udp); + param.put("src_port", requestPushStreamMsg.getSrcPort()); + param.put("pt", requestPushStreamMsg.getPt()); + param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0"); + param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0"); + JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); + // 回复消息 + responsePushStream(jsonObject, fromId, serial); + } + + private void responsePushStream(JSONObject content, String toId, String serial) { + + WVPResult result = new WVPResult<>(); + result.setCode(0); + result.setData(content); + + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId, + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result); + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + /** + * 处理收到的请求sendItem的请求 + */ + private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) { + MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId()); + if (mediaServerItem == null) { + logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId()); + + WVPResult result = new WVPResult<>(); + result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND); + result.setMsg("流媒体不存在"); + + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId, + WvpRedisMsgCmd.GET_SEND_ITEM, serial, result); + + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + return; + } + // 确定流是否在线 + boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream()); + if (streamReady) { + logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream()); + responseSendItem(mediaServerItem, content, toId, serial); + }else { + // 流已经离线 + // 发送redis消息以使设备上线 + logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream()); + + String taskKey = UUID.randomUUID().toString(); + // 设置超时 + dynamicTask.startDelay(taskKey, ()->{ + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream()); + WVPResult result = new WVPResult<>(); + result.setCode(ERROR_CODE_TIMEOUT); + WvpRedisMsg response = WvpRedisMsg.getResponseInstance( + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result + ); + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + }, userSetting.getPlatformPlayTimeout()); + + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(content.getApp(), content.getStream(), true, "rtsp", mediaServerItem.getId()); + + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ + dynamicTask.stop(taskKey); + responseSendItem(mediaServerItem, content, toId, serial); + }); + + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(), + content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(), + content.getMediaServerId()); + + String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; + logger.info("[redis发送通知] 推流被请求 {}: {}/{}", key, messageForPushChannel.getApp(), messageForPushChannel.getStream()); + redisTemplate.convertAndSend(key, JSON.toJSON(messageForPushChannel)); + +// redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); + + } + } + + /** + * 将获取到的sendItem发送出去 + */ + private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) { + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), + content.getPort(), content.getSsrc(), content.getPlatformId(), + content.getApp(), content.getStream(), content.getChannelId(), + content.getTcp(), content.getRtcp()); + + WVPResult result = new WVPResult<>(); + result.setCode(0); + ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg(); + responseSendItemMsg.setSendRtpItem(sendRtpItem); + responseSendItemMsg.setMediaServerItem(mediaServerItem); + result.setData(responseSendItemMsg); + + WvpRedisMsg response = WvpRedisMsg.getResponseInstance( + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result + ); + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + /** + * 发送消息要求下级生成推流信息 + * @param serverId 下级服务ID + * @param app 应用名 + * @param stream 流ID + * @param ip 目标IP + * @param port 目标端口 + * @param ssrc ssrc + * @param platformId 平台国标编号 + * @param channelId 通道ID + * @param isTcp 是否使用TCP + * @param callback 得到信息的回调 + */ + public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc, + String platformId, String channelId, boolean isTcp, boolean rtcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) { + RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance( + serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, rtcp, platformName); + requestSendItemMsg.setServerId(serverId); + String key = UUID.randomUUID().toString(); + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM, + key, requestSendItemMsg); + + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg); + logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject); + callbacks.put(key, callback); + callbacksForError.put(key, errorCallback); + dynamicTask.startDelay(key, ()->{ + callbacks.remove(key); + callbacksForError.remove(key); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ERROR_CODE_TIMEOUT); + wvpResult.setMsg("timeout"); + errorCallback.handler(wvpResult); + }, userSetting.getPlatformPlayTimeout()); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + /** + * 发送请求推流的消息 + * @param param 推流参数 + * @param callback 回调 + */ + public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) { + String key = UUID.randomUUID().toString(); + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param); + + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg); + logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject); + dynamicTask.startDelay(key, ()->{ + callbacksForStartSendRtpStream.remove(key); + callbacksForError.remove(key); + }, userSetting.getPlatformPlayTimeout()); + callbacksForStartSendRtpStream.put(key, callback); + callbacksForError.put(key, (wvpResult)->{ + logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg()); + callbacksForStartSendRtpStream.remove(key); + callbacksForError.remove(key); + }); + redisTemplate.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisGpsMsgListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisGpsMsgListener.java new file mode 100644 index 0000000..091d77b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisGpsMsgListener.java @@ -0,0 +1,77 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 接收来自redis的GPS更新通知 + */ +@Component +public class RedisGpsMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + @Override + public void onMessage(@NotNull Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class); + // 只是放入redis缓存起来 + redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS的ALARM通知] 异常内容: ", e); + } + } + }); + } + } + + /** + * 定时将经纬度更新到数据库 + */ +// @Scheduled(fixedRate = 2 * 1000) //每2秒执行一次 + public void execute(){ + List gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo(); + if (gpsMsgInfo.size() > 0) { + storager.updateStreamGPS(gpsMsgInfo); + for (GPSMsgInfo msgInfo : gpsMsgInfo) { + msgInfo.setStored(true); + redisCatchStorage.updateGpsMsgInfo(msgInfo); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamResponseListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamResponseListener.java new file mode 100644 index 0000000..40ad879 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamResponseListener.java @@ -0,0 +1,75 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.yfd.monitor.service.bean.MessageForPushChannelResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 接收redis返回的推流结果 + */ +@Component +public class RedisPushStreamResponseListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamResponseListener.class); + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + private Map responseEvents = new ConcurrentHashMap<>(); + + public interface PushStreamResponseEvent{ + void run(MessageForPushChannelResponse response); + } + + @Override + public void onMessage(Message message, byte[] bytes) { + logger.info("[REDIS消息-请求推流结果]: {}", new String(message.getBody())); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + MessageForPushChannelResponse response = JSON.parseObject(new String(msg.getBody()), MessageForPushChannelResponse.class); + if (response == null || ObjectUtils.isEmpty(response.getApp()) || ObjectUtils.isEmpty(response.getStream())){ + logger.info("[REDIS消息-请求推流结果]:参数不全"); + continue; + } + // 查看正在等待的invite消息 + if (responseEvents.get(response.getApp() + response.getStream()) != null) { + responseEvents.get(response.getApp() + response.getStream()).run(response); + } + }catch (Exception e) { + logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-请求推流结果] 异常内容: ", e); + } + } + }); + } + } + + public void addEvent(String app, String stream, PushStreamResponseEvent callback) { + responseEvents.put(app + stream, callback); + } + + public void removeEvent(String app, String stream) { + responseEvents.remove(app + stream); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamStatusListMsgListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamStatusListMsgListener.java new file mode 100644 index 0000000..1cf9359 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamStatusListMsgListener.java @@ -0,0 +1,105 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.utils.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @Auther: JiangFeng + * @Date: 2022/8/16 11:32 + * @Description: 接收redis发送的推流设备列表更新通知 + */ +@Component +public class RedisPushStreamStatusListMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusListMsgListener.class); + @Resource + private IMediaServerService mediaServerService; + + @Resource + private IStreamPushService streamPushService; + @Resource + private IGbStreamService gbStreamService; + + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + logger.info("[REDIS消息-推流设备列表更新]: {}", new String(message.getBody())); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + List streamPushItems = JSON.parseArray(new String(msg.getBody()), StreamPushItem.class); + //查询全部的app+stream 用于判断是添加还是修改 + List allAppAndStream = streamPushService.getAllAppAndStream(); + + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ + List streamPushItemForSave = new ArrayList<>(); + List streamPushItemForUpdate = new ArrayList<>(); + for (StreamPushItem streamPushItem : streamPushItems) { + String app = streamPushItem.getApp(); + String stream = streamPushItem.getStream(); + boolean contains = allAppAndStream.contains(app + stream); + //不存在就添加 + if (!contains) { + streamPushItem.setStreamType("push"); + streamPushItem.setCreateTime(DateUtil.getNow()); + streamPushItem.setMediaServerId(mediaServerService.getDefaultMediaServer().getId()); + streamPushItem.setOriginType(2); + streamPushItem.setOriginTypeStr("rtsp_push"); + streamPushItem.setTotalReaderCount("0"); + streamPushItemForSave.add(streamPushItem); + } else { + //存在就只修改 name和gbId + streamPushItemForUpdate.add(streamPushItem); + } + } + if (streamPushItemForSave.size() > 0) { + + logger.info("添加{}条",streamPushItemForSave.size()); + logger.info(JSONObject.toJSONString(streamPushItemForSave)); + streamPushService.batchAdd(streamPushItemForSave); + + } + if(streamPushItemForUpdate.size()>0){ + logger.info("修改{}条",streamPushItemForUpdate.size()); + logger.info(JSONObject.toJSONString(streamPushItemForUpdate)); + gbStreamService.updateGbIdOrName(streamPushItemForUpdate); + } + }catch (Exception e) { + logger.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e); + } + } + }); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamStatusMsgListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamStatusMsgListener.java new file mode 100644 index 0000000..fbd444d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisPushStreamStatusMsgListener.java @@ -0,0 +1,100 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.service.bean.PushStreamStatusChangeFromRedisDto; +import com.yfd.monitor.storager.IRedisCatchStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 接收redis发送的推流设备上线下线通知 + */ +@Component +public class RedisPushStreamStatusMsgListener implements MessageListener, ApplicationRunner { + + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusMsgListener.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private DynamicTask dynamicTask; + + + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + logger.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody())); + taskQueue.offer(message); + + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + PushStreamStatusChangeFromRedisDto statusChangeFromPushStream = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); + if (statusChangeFromPushStream == null) { + logger.warn("[REDIS消息]推流设备状态变化消息解析失败"); + continue; + } + // 取消定时任务 + dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED); + if (statusChangeFromPushStream.isSetAllOffline()) { + // 所有设备离线 + streamPushService.allStreamOffline(); + } + if (statusChangeFromPushStream.getOfflineStreams() != null + && statusChangeFromPushStream.getOfflineStreams().size() > 0) { + // 更新部分设备离线 + streamPushService.offline(statusChangeFromPushStream.getOfflineStreams()); + } + if (statusChangeFromPushStream.getOnlineStreams() != null && + statusChangeFromPushStream.getOnlineStreams().size() > 0) { + // 更新部分设备上线 + streamPushService.online(statusChangeFromPushStream.getOnlineStreams()); + } + }catch (Exception e) { + logger.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e); + } + } + }); + } + } + + @Override + public void run(ApplicationArguments args) throws Exception { + // 启动时设置所有推流通道离线,发起查询请求 + redisCatchStorage.sendStreamPushRequestedMsgForStatus(); + dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{ + logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); + // 五秒收不到请求就设置通道离线,然后通知上级离线 + streamPushService.allStreamOffline(); + }, 5000); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisStreamMsgListener.java b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisStreamMsgListener.java new file mode 100644 index 0000000..cbb74ea --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/service/redisMsg/RedisStreamMsgListener.java @@ -0,0 +1,91 @@ +package com.yfd.monitor.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.UserSetting; + +import com.yfd.monitor.media.zlm.ZLMMediaListManager; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 接收其他wvp发送流变化通知 + */ +@Component +public class RedisStreamMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMMediaListManager zlmMediaListManager; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + JSONObject steamMsgJson = JSON.parseObject(msg.getBody(), JSONObject.class); + if (steamMsgJson == null) { + logger.warn("[收到redis 流变化]消息解析失败"); + continue; + } + String serverId = steamMsgJson.getString("serverId"); + + if (userSetting.getServerId().equals(serverId)) { + // 自己发送的消息忽略即可 + continue; + } + logger.info("[收到redis 流变化]: {}", new String(message.getBody())); + String app = steamMsgJson.getString("app"); + String stream = steamMsgJson.getString("stream"); + boolean register = steamMsgJson.getBoolean("register"); + String mediaServerId = steamMsgJson.getString("mediaServerId"); + OnStreamChangedHookParam onStreamChangedHookParam = new OnStreamChangedHookParam(); + onStreamChangedHookParam.setSeverId(serverId); + onStreamChangedHookParam.setApp(app); + onStreamChangedHookParam.setStream(stream); + onStreamChangedHookParam.setRegist(register); + onStreamChangedHookParam.setMediaServerId(mediaServerId); + onStreamChangedHookParam.setCreateStamp(System.currentTimeMillis()/1000); + onStreamChangedHookParam.setAliveSecond(0L); + onStreamChangedHookParam.setTotalReaderCount("0"); + onStreamChangedHookParam.setOriginType(0); + onStreamChangedHookParam.setOriginTypeStr("0"); + onStreamChangedHookParam.setOriginTypeStr("unknown"); + if (register) { + zlmMediaListManager.addPush(onStreamChangedHookParam); + }else { + zlmMediaListManager.removeMedia(app, stream); + } + }catch (Exception e) { + logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); + logger.error("[REDIS消息-流变化] 异常内容: ", e); + } + } + }); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/IRedisCatchStorage.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/IRedisCatchStorage.java new file mode 100644 index 0000000..99ebe85 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/IRedisCatchStorage.java @@ -0,0 +1,283 @@ +package com.yfd.monitor.storager; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.common.SystemAllInfo; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.dto.*; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.service.bean.ThirdPartyGB; +import com.yfd.monitor.storager.dao.dto.PlatformRegisterInfo; + +import javax.sip.header.WWWAuthenticateHeader; +import java.util.List; +import java.util.Map; + +public interface IRedisCatchStorage { + + /** + * 计数器。为cseq进行计数 + * + * @return + */ + Long getCSEQ(); + + /** + * 开始播放时将流存入 + * + * @param stream 流信息 + * @return + */ + boolean startPlay(StreamInfo stream); + + + /** + * 停止播放时删除 + * + * @return + */ + boolean stopPlay(StreamInfo streamInfo); + + /** + * 查询播放列表 + * @return + */ + StreamInfo queryPlay(StreamInfo streamInfo); + + StreamInfo queryPlayByStreamId(String steamId); + + StreamInfo queryPlayByDevice(String deviceId, String channelId); + + Map queryPlayByDeviceId(String deviceId); + + boolean startPlayback(StreamInfo stream, String callId); + + boolean stopPlayback(String deviceId, String channelId, String stream, String callId); + + StreamInfo queryPlayback(String deviceId, String channelID, String stream, String callId); + + String queryPlaybackForKey(String deviceId, String channelId, String stream, String callId); + + void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch); + + ParentPlatformCatch queryPlatformCatchInfo(String platformGbId); + + void updatePlatformWWwInfo(String platformGbId, cn.hutool.json.JSONObject www); + + Object queryPlatformWWwInfo(String platformGbId); + + void delPlatformCatchInfo(String platformGbId); + + void delPlatformKeepalive(String platformGbId); + + void delPlatformRegister(String platformGbId); + + void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo); + + PlatformRegisterInfo queryPlatformRegisterInfo(String callId); + + void delPlatformRegisterInfo(String callId); + + void cleanPlatformRegisterInfos(); + + void updateSendRTPSever(SendRtpItem sendRtpItem); + + /** + * 查询RTP推送信息缓存 + * @param platformGbId + * @param channelId + * @return sendRtpItem + */ + SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId); + + List querySendRTPServer(String platformGbId); + + /** + * 删除RTP推送信息缓存 + * @param platformGbId + * @param channelId + */ + void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId); + + /** + * 查询某个通道是否存在上级点播(RTP推送) + * @param channelId + */ + boolean isChannelSendingRTP(String channelId); + + /** + * 清空某个设备的所有缓存 + * @param deviceId 设备ID + */ + void clearCatchByDeviceId(String deviceId); + + /** + * 在redis添加wvp的信息 + */ + void updateWVPInfo(JSONObject jsonObject, int time); + + /** + * 发送推流生成与推流消失消息 + * @param jsonObject 消息内容 + */ + void sendStreamChangeMsg(String type, JSONObject jsonObject); + + /** + * 发送报警消息 + * @param msg 消息内容 + */ + void sendAlarmMsg(AlarmChannelMessage msg); + + /** + * 添加流信息到redis + * @param mediaServerItem + * @param app + * @param streamId + */ + void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, OnStreamChangedHookParam item); + + /** + * 移除流信息从redis + * @param mediaServerId + * @param app + * @param streamId + */ + void removeStream(String mediaServerId, String type, String app, String streamId); + + + /** + * 移除流信息从redis + * @param mediaServerId + */ + void removeStream(String mediaServerId, String type); + + /** + * 开始下载录像时存入 + * @param streamInfo + */ + boolean startDownload(StreamInfo streamInfo, String callId); + + StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId); + + boolean stopDownload(String deviceId, String channelId, String stream, String callId); + + /** + * 查找第三方系统留下的国标预设值 + * @param queryKey + * @return + */ + ThirdPartyGB queryMemberNoGBId(String queryKey); + + List getStreams(String mediaServerId, String pull); + + /** + * 将device信息写入redis + * @param device + */ + void updateDevice(Device device); + + void removeDevice(String deviceId); + + /** + * 获取Device + */ + Device getDevice(String deviceId); + + void resetAllCSEQ(); + + void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo); + + GPSMsgInfo getGpsMsgInfo(String gbId); + List getAllGpsMsgInfo(); + + Long getSN(String method); + + void resetAllSN(); + + OnStreamChangedHookParam getStreamInfo(String app, String streamId, String mediaServerId); + + void addCpuInfo(double cpuInfo); + + void addMemInfo(double memInfo); + + void addNetInfo(Map networkInterfaces); + + void sendMobilePositionMsg(JSONObject jsonObject); + + void sendStreamPushRequestedMsg(MessageForPushChannel messageForPushChannel); + + /** + * 判断设备状态 + * @param deviceId 设备ID + * @return + */ + boolean deviceIsOnline(String deviceId); + + /** + * 存储推流的鉴权信息 + * @param app 应用名 + * @param stream 流 + * @param streamAuthorityInfo 鉴权信息 + */ + void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo); + + /** + * 移除推流的鉴权信息 + * @param app 应用名 + * @param streamId 流 + */ + void removeStreamAuthorityInfo(String app, String streamId); + + /** + * 获取推流的鉴权信息 + * @param app 应用名 + * @param stream 流 + * @return + */ + StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream); + + List getAllStreamAuthorityInfo(); + + /** + * 发送redis消息 查询所有推流设备的状态 + */ + void sendStreamPushRequestedMsgForStatus(); + + List querySendRTPServerByChnnelId(String channelId); + + List querySendRTPServerByStream(String stream); + + SystemAllInfo getSystemInfo(); + + int getPushStreamCount(String id); + + int getProxyStreamCount(String id); + + int getGbReceiveCount(String id); + + int getGbSendCount(String id); + + void addDiskInfo(List> diskInfo); + + List queryAllSendRTPServer(); + + List getAllDevices(); + + void removeAllDevice(); + + void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online); + MediaInfo getStreamInfo1(String app, String streamId, String mediaServerId); + + /** + * 添加流信息到redis + * @param mediaServerItem + * @param app + * @param streamId + */ + void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, MediaInfo item); + + void sendPlatformStartPlayMsg(MessageForPushChannel messageForPushChannel); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/IVideoManagerStorage.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/IVideoManagerStorage.java new file mode 100644 index 0000000..c360df3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/IVideoManagerStorage.java @@ -0,0 +1,380 @@ +package com.yfd.monitor.storager; + +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.storager.dao.dto.ChannelSourceInfo; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import com.yfd.monitor.web.gdw2019.dto.DeviceChannelExtend; +import com.github.pagehelper.PageInfo; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * @description:视频设备数据存储接口 + * @date: 2020年5月6日 下午2:14:31 + */ +@SuppressWarnings("rawtypes") +public interface IVideoManagerStorage { + + /** + * 根据设备ID判断设备是否存在 + * + * @param deviceId 设备ID + * @return true:存在 false:不存在 + */ + boolean exists(String deviceId); + + /** + * 开始播放 + * @param deviceId 设备id + * @param channelId 通道ID + * @param streamId 流地址 + */ + void startPlay(String deviceId, String channelId, String streamId); + + /** + * 停止播放 + * @param deviceId 设备id + * @param channelId 通道ID + */ + void stopPlay(String deviceId, String channelId); + + /** + * 获取设备 + * + * @param deviceId 设备ID + * @return DShadow 设备对象 + */ + Device queryVideoDevice(String deviceId); + + + /** + * 根据摄像头ID,查询NVR设备 + * + * @param deviceId 摄像头设备ID + * @return DShadow NVR设备对象 + */ + Device queryNvrDeviceByDeviceId(String deviceId,String channelId); + + /** + * 获取某个设备的通道列表 + * + * @param deviceId 设备ID + * @param page 分页 当前页 + * @param count 每页数量 + * @return + */ + PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count); + + List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit); + + + /** + * 获取某个设备的通道列表 + * + * @param deviceId 设备ID + * @return + */ + List queryChannelsByDeviceId(String deviceId, Boolean online, List channelIds); + List queryOnlineChannelsByDeviceId(String deviceId); + + /** + * 获取某个设备的通道 + * @param deviceId 设备ID + * @param channelId 通道ID + */ + DeviceChannel queryChannel(String deviceId, String channelId); + + /** + * 删除通道 + * @param deviceId 设备ID + * @param channelId 通道ID + */ + int delChannel(String deviceId, String channelId); + + /** + * 获取多个设备 + * @param page 当前页数 + * @param count 每页数量 + * @return List 设备对象数组 + */ + PageInfo queryVideoDeviceList(int page, int count, Boolean online); + + /** + * 获取多个设备 + * + * @return List 设备对象数组 + */ + List queryVideoDeviceList(Boolean online); + + + + /** + * 查询子设备 + * + * @param deviceId + * @param channelId + * @param page + * @param count + * @return + */ + PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, Boolean online, int page, int count); + + + /** + * 清空通道 + * @param deviceId + */ + void cleanChannelsForDevice(String deviceId); + + + /** + * 更新上级平台 + * @param parentPlatform + */ + boolean updateParentPlatform(ParentPlatform parentPlatform); + + + /** + * 添加上级平台 + * @param parentPlatform + */ + boolean addParentPlatform(ParentPlatform parentPlatform); + + /** + * 删除上级平台 + * @param parentPlatform + */ + boolean deleteParentPlatform(ParentPlatform parentPlatform); + + /** + * 获取所有已启用的平台 + * @return + */ + List queryEnableParentPlatformList(boolean enable); + + /** + * 获取上级平台 + * @param platformGbId + * @return + */ + ParentPlatform queryParentPlatByServerGBId(String platformGbId); + + /** + * 所有平台离线 + */ + void outlineForAllParentPlatform(); + + /** + * 查询通道信息,不区分设备(已关联平台或全部) + */ + PageInfo queryAllChannelList(int page, int count, String query, Boolean online, Boolean channelType, String platformId, String catalogId); + + /** + * 查询设备的通道信息 + */ + List queryChannelListInParentPlatform(String platformId); + + + + /** + * 移除上级平台的通道信息 + * @param platformId + * @param channelReduces + * @return + */ + int delChannelForGB(String platformId, List channelReduces); + + + DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); + + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + List queryStreamInParentPlatformAndCatalog(String platformId, String catalogId); + + Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); + + /** + * 针对deviceinfo指令的查询接口 + * @param platformId 平台id + * @param channelId 通道id + * @return 设备信息 + */ + Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId); + + + /** + * 移除代理流 + * @param app + * @param stream + * @return + */ + int deleteStreamProxy(String app, String stream); + + /** + * 按照是否启用获取代理流 + * @param enable + * @return + */ + List getStreamProxyListForEnable(boolean enable); + + /** + * 按照是app和stream获取代理流 + * @param app + * @param stream + * @return + */ + StreamProxyItem queryStreamProxy(String app, String stream); + + /** + * 获取代理流 + * @param page + * @param count + * @return + */ + PageInfo queryStreamProxyList(Integer page, Integer count); + + /** + * 根据国标ID获取平台关联的直播流 + * @param platformId + * @param channelId + * @return + */ + GbStream queryStreamInParentPlatform(String platformId, String channelId); + + /** + * 获取平台关联的直播流 + * @param platformId + * @return + */ + List queryGbStreamListInPlatform(String platformId); + + /** + * 移除单个推流 + * @param app + * @param stream + */ + int removeMedia(String app, String stream); + + /** + * 设置流离线 + */ + int mediaOffline(String app, String streamId); + + /** + * 设置流上线 + */ + int mediaOnline(String app, String streamId); + + /** + * 设置平台在线/离线 + */ + void updateParentPlatformStatus(String platformGbID, boolean online); + + /** + * 根据媒体ID获取启用/不启用的代理列表 + * @param id 媒体ID + * @param enable 启用/不启用 + * @return + */ + List getStreamProxyListForEnableInMediaServer(String id, boolean enable); + + /** + * 根据通道ID获取其所在设备 + * @param channelId 通道ID + * @return + */ + Device queryVideoDeviceByChannelId(String channelId); + + /** + * 通道上线 + * @param channelId 通道ID + */ + void deviceChannelOnline(String deviceId, String channelId); + + /** + * 通道离线 + * @param channelId 通道ID + */ + void deviceChannelOffline(String deviceId, String channelId); + + /** + * 通过app与stream获取StreamProxy + * @param app + * @param streamId + * @return + */ + StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId); + + /** + * catlog查询结束后完全重写通道信息 + * @param deviceId + * @param deviceChannelList + */ + boolean resetChannels(String deviceId, List deviceChannelList); + + boolean updateChannels(String deviceId, List deviceChannelList); + + /** + * 获取目录信息 + * @param platformId + * @param parentId + * @return + */ + List getChildrenCatalogByPlatform(String platformId, String parentId); + + int addCatalog(PlatformCatalog platformCatalog); + + PlatformCatalog getCatalog(String id); + + int delCatalog(String id); + + int updateCatalog(PlatformCatalog platformCatalog); + + int setDefaultCatalog(String platformId, String catalogId); + + List queryCatalogInPlatform(String serverGBId); + + int delRelation(PlatformCatalog platformCatalog); + + int updateStreamGPS(List gpsMsgInfo); + + List queryPlatFormListForGBWithGBId(String channelId, List platforms); + + List queryPlatFormListForStreamWithGBId(String app, String stream, List platforms); + + GbStream getGbStream(String app, String streamId); + + void delCatalogByPlatformId(String serverGBId); + + void delRelationByPlatformId(String serverGBId); + + PlatformCatalog queryDefaultCatalogInPlatform(String platformId); + + List getChannelSource(String platformId, String gbId); + + void updateChannelPosition(DeviceChannel deviceChannel); + + void cleanContentForPlatform(String serverGBId); + + List queryChannelWithCatalog(String CatalogId); + + List queryChannelWithPlatform(String serverGBId); + + List queryChannelsByDeviceId(String serial, List channelIds, Boolean online); + + List queryEnablePlatformListWithAsMessageChannel(); + + List queryDeviceWithAsMessageChannel(); + + boolean updatePatroldeviceTime(String id, String prepos); + + List getRiisPatrolDeviceByCode(String deviceId); + + List getRiisPatrolDeviceByRobot(String robotCode); + + List> queryHistoryAlarmlog(String code, String userCode, String type, String beginTime, + String endTime, String level, int fromIndex, int size); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceAlarmMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceAlarmMapper.java new file mode 100644 index 0000000..6bcbd8b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceAlarmMapper.java @@ -0,0 +1,66 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * 用于存储设备的报警信息 + */ +@Mapper +@Repository +public interface DeviceAlarmMapper { + + @Insert("INSERT INTO mon_device_alarm (deviceId, channelId, alarmPriority, alarmMethod, alarmTime, alarmDescription, longitude, latitude, alarmType , createTime ) " + + "VALUES (#{deviceId}, #{channelId}, #{alarmPriority}, #{alarmMethod}, #{alarmTime}, #{alarmDescription}, #{longitude}, #{latitude}, #{alarmType}, #{createTime})") + int add(DeviceAlarm alarm); + + + @Select( value = {" "}) + List query(String deviceId, String alarmPriority, String alarmMethod, + String alarmType, String startTime, String endTime); + + + @Delete(" " + ) + int clearAlarmBeforeTime(Integer id, List deviceIdList, String time); + + @Select( value = {" "}) + List> queryHistoryAlarmlog(String code, String userCode, String type, String beginTime, String endTime, String level, int fromIndex, int size); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceChannelMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceChannelMapper.java new file mode 100644 index 0000000..82d2a38 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceChannelMapper.java @@ -0,0 +1,472 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.DeviceChannelInPlatform; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import com.yfd.monitor.web.gdw2019.dto.DeviceChannelExtend; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 用于存储设备通道信息 + */ +@Mapper +@Repository +public interface DeviceChannelMapper { + + @Insert("INSERT INTO mon_device_channel (channelId, deviceId, name, manufacture, model, owner, civilCode, block, " + + "address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + + "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " + + "longitudeWgs84, latitudeWgs84, hasAudio, createTime, updateTime, businessGroupId, gpsTime) " + + "VALUES (#{channelId}, #{deviceId}, #{name}, #{manufacture}, #{model}, #{owner}, #{civilCode}, #{block}," + + "#{address}, #{parental}, #{parentId}, #{safetyWay}, #{registerWay}, #{certNum}, #{certifiable}, #{errCode}, #{secrecy}, " + + "#{ipAddress}, #{port}, #{password}, #{PTZType}, #{status}, #{streamId}, #{longitude}, #{latitude}, #{longitudeGcj02}, " + + "#{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{hasAudio}, #{createTime}, #{updateTime}, #{businessGroupId}, #{gpsTime})") + int add(DeviceChannel channel); + + @Update(value = {" "}) + int update(DeviceChannel channel); + + @Select(value = {" "}) + List queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List channelIds); + + @Select(value = {" "}) + List queryChannelsWithDeviceInfo(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List channelIds); + + + @Select(value = {" "}) + List queryChannelsByDeviceIdWithStartAndLimit(String deviceId,List channelIds, String parentChannelId, String query, + Boolean hasSubChannel, Boolean online, int start, int limit); + + @Select("SELECT * FROM mon_device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}") + DeviceChannel queryChannel(String deviceId, String channelId); + + //SELECT * FROM mon_device_channel WHERE (ipAddress=#{deviceIp} OR address = #{deviceIp}) AND port != 0 AND parentId is not null + @Select("SELECT * FROM mon_device_channel WHERE (ipAddress=#{deviceIp} OR address = #{deviceIp}) AND parentId is not null ORDER BY `port` DESC") + List queryChannelByDeviceIp(String deviceIp); + + @Delete("DELETE FROM mon_device_channel WHERE deviceId=#{deviceId}") + int cleanChannelsByDeviceId(String deviceId); + + @Delete("DELETE FROM mon_device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}") + int del(String deviceId, String channelId); + + @Update(value = {"UPDATE mon_device_channel SET streamId=null WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + void stopPlay(String deviceId, String channelId); + + @Update(value = {"UPDATE mon_device_channel SET streamId=#{streamId} WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + void startPlay(String deviceId, String channelId, String streamId); + + @Select(value = {" "}) + List queryChannelListInAll(String query, Boolean online, Boolean hasSubChannel, String platformId, String catalogId); + + @Select(value = {" "}) + List queryChannelByPlatformId(String platformId); + + + @Select("SELECT * FROM mon_device_channel WHERE channelId=#{channelId}") + List queryChannelByChannelId( String channelId); + + @Update(value = {"UPDATE mon_device_channel SET status=0 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + void offline(String deviceId, String channelId); + + @Update(value = {"UPDATE mon_device_channel SET status=0 WHERE deviceId=#{deviceId}"}) + void offlineByDeviceId(String deviceId); + + @Insert("") + int batchAdd(List addChannels); + + @Update(value = {"UPDATE mon_device_channel SET status=1 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + void online(String deviceId, String channelId); + + @Update({""}) + int batchUpdate(List updateChannels); + + + @Select("SELECT * FROM mon_device_channel WHERE deviceId=#{deviceId} AND status=1") + List queryOnlineChannelsByDeviceId(String deviceId); + + @Delete(value = {" "}) + int cleanChannelsNotInList(String deviceId, List channels); + + @Update(" update mon_device_channel" + + " set subCount = (select *" + + " from (select count(0)" + + " from mon_device_channel" + + " where deviceId = #{deviceId} and parentId = #{channelId}) as temp)" + + " where deviceId = #{deviceId} " + + " and channelId = #{channelId}") + int updateChannelSubCount(String deviceId, String channelId); + + @Update(value = {" "}) + void updatePosition(DeviceChannel deviceChannel); + + @Select("SELECT * FROM mon_device_channel WHERE length(trim(streamId)) > 0") + List getAllChannelInPlay(); + + @Select("select * from mon_device_channel where longitude*latitude > 0 and deviceId = #{deviceId}") + List getAllChannelWithCoordinate(String deviceId); + + + @Select(value = {" "}) + List getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length); + + @Select(value = {" "}) + List getChannelsByCivilCode(String deviceId, String parentId); + + @Select("select min(length(channelId)) as minLength " + + "from mon_device_channel " + + "where deviceId=#{deviceId}") + Integer getChannelMinLength(String deviceId); + + @Select("select * from mon_device_channel where deviceId=#{deviceId} and civilCode not in " + + "(select civilCode from mon_device_channel where deviceId=#{deviceId} group by civilCode)") + List getChannelWithoutCiviCode(String deviceId); + + @Select("select * from mon_device_channel where deviceId=#{deviceId} and SUBSTRING(channelId, 11, 3)=#{typeCode}") + List getBusinessGroups(String deviceId, String typeCode); + +// @Select("select dc.id, dc.channelId, CONCAT(SUBSTRING('100110000101040000', 1, CHAR_LENGTH('100110000101040000') - 4), SUBSTRING(sp.patroldevice_code, CHAR_LENGTH(sp.patroldevice_code) - 3, 4)) deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " + +// " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " + +// " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " + +// " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " + +// " from mon_device_channel dc" + +// " left join mon_platform_gb_channel pgc on dc.id = pgc.deviceChannelId" + +// " left join iis_substation_patroldevice sp on sp.internationalId = dc.deviceId" + +// " left join mon_platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" + +// " where pc.id=#{catalogId} and sp.patroldevice_code is not null") +@Select("select dc.id, dc.channelId, sp.patroldevice_code deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " + + " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " + + " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " + + " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " + + " from mon_device_channel dc" + + " left join mon_platform_gb_channel pgc on dc.id = pgc.deviceChannelId" + + " left join iis_substation_patroldevice sp on sp.internationalId = dc.deviceId" + + " left join mon_platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" + + " where pc.id=#{catalogId} and sp.patroldevice_code is not null") + List queryChannelWithCatalog(String catalogId); + + +// @Select("select dc.id, dc.channelId,CONCAT(SUBSTRING('100110000101040000', 1, CHAR_LENGTH('100110000101040000') - 4), SUBSTRING(sp.patroldevice_code, CHAR_LENGTH(sp.patroldevice_code) - 3, 4)) deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " + +// " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " + +// " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " + +// " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " + +// " from mon_device_channel dc" + +// " left join iis_substation_patroldevice sp on sp.internationalId = dc.deviceId" + +// " left join mon_platform_gb_channel pgc on dc.id = pgc.deviceChannelId" + +// " left join mon_platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" + +// " where pc.platformId=#{platformId} and sp.patroldevice_code is not null") +@Select("select dc.id, dc.channelId,sp.patroldevice_code deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " + + " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " + + " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " + + " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " + + " from mon_device_channel dc" + + " left join iis_substation_patroldevice sp on sp.internationalId = dc.deviceId" + + " left join mon_platform_gb_channel pgc on dc.id = pgc.deviceChannelId" + + " left join mon_platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" + + " where pc.platformId=#{platformId} and sp.patroldevice_code is not null") + List queryChannelWithPlatform(String platformId); + + + @Select("select * from mon_device_channel where deviceId = #{deviceId}") + List queryAllChannels(String deviceId); + + @Select("select b.* from iis_substation_patroldevice a join mon_device_channel b on (a.patroldevice_code=b.deviceId) where a.online='1' and a.type='11' order by b.channelId") + List queryChannelByOnLineNvr(); + + + @Select("select count(1) as total, sum(status) as online from mon_device_channel") + ResourceBaceInfo getOverview(); + + @Select("select channelId" + + ", deviceId" + + ", latitude" + + ", longitude" + + ", latitudeWgs84" + + ", longitudeWgs84" + + ", latitudeGcj02" + + ", longitudeGcj02 " + + "from mon_device_channel where deviceId = #{deviceId} " + + "and latitude != 0 " + + "and longitude != 0 " + + "and (latitudeGcj02 = 0 or latitudeWgs84 = 0 or longitudeWgs84 = 0 or longitudeGcj02 = 0)") + List getChannelsWithoutTransform(String deviceId); + + @Select("select de.* from mon_device de left join mon_device_channel dc on de.deviceId = dc.deviceId where dc.channelId=#{channelId}") + List getDeviceByChannelId(String channelId); + + + @Delete({""}) + int batchDel(List deleteChannelList); + + @Update({""}) + int batchOnline(List channels); + + @Update({""}) + int batchOffline(List channels); + + @Select("select dc.* from mon_device_channel dc INNER JOIN iis_substation_patroldevice sp ON sp.internationalId=dc.deviceId where sp.patroldevice_code = #{deviceId}") + List queryChannelList(String code); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceMapper.java new file mode 100644 index 0000000..ec75326 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceMapper.java @@ -0,0 +1,462 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * 用于存储设备信息 + */ +@Mapper +@Repository +public interface DeviceMapper { + + @Select("SELECT " + + "deviceId, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "asMessageChannel," + + "geoCoordSys," + + "treeType," + + "online," + + "mediaServerId," + + "(SELECT count(0) FROM mon_device_channel WHERE deviceId=mon_device.deviceId) as channelCount "+ + " FROM mon_device WHERE deviceId = #{deviceId}") + Device getDeviceByDeviceId(String deviceId); + + @Insert("INSERT INTO mon_device (" + + "deviceId, " + + "name, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "keepaliveIntervalTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "asMessageChannel," + + "geoCoordSys," + + "treeType," + + "online" + + ") VALUES (" + + "#{deviceId}," + + "#{name}," + + "#{manufacturer}," + + "#{model}," + + "#{firmware}," + + "#{transport}," + + "#{streamMode}," + + "#{ip}," + + "#{sdpIp}," + + "#{localIp}," + + "#{port}," + + "#{hostAddress}," + + "#{expires}," + + "#{registerTime}," + + "#{keepaliveTime}," + + "#{keepaliveIntervalTime}," + + "#{createTime}," + + "#{updateTime}," + + "#{charset}," + + "#{subscribeCycleForCatalog}," + + "#{subscribeCycleForMobilePosition}," + + "#{mobilePositionSubmissionInterval}," + + "#{subscribeCycleForAlarm}," + + "#{ssrcCheck}," + + "#{asMessageChannel}," + + "#{geoCoordSys}," + + "#{treeType}," + + "#{online}" + + ")") + int add(Device device); + + @Select("SELECT " + + "count(1) as count" + + " FROM iis_substation_patroldevice WHERE internationalId = #{deviceId}") + int getRiisPatrolDevice(String deviceId); + + @Select("SELECT " + + "patroldevice_id, " + + "patroldevice_code, " + + "rtspurl, " + + "ipaddr, " + + "channelinfo, " + + "type " + + " FROM iis_substation_patroldevice WHERE internationalId=#{deviceId}") + List getRiisPatrolDeviceByCode(String deviceId); + + @Select("SELECT " + + "patroldevice_id, " + + "patroldevice_code, " + + "preset, " + + "presettime, " + + "DATE_FORMAT(changepresettime, '%Y-%m-%d %H:%i:%s') changepresettime " + + " FROM iis_substation_patroldevice WHERE preset is not null and presettime is not null and changepresettime is not null and type='10' and online = '1' and working='0' ") + List getRiisPatrolDevices(); + + + @Insert("INSERT INTO iis_substation_patroldevice (" + + "patroldevice_id, " + //UUID + "patroldevice_code, " +//摄像机编号 与 视频摄像头编号一致 + "internationalId, " +//摄像头国标编码 + "patroldevice_name, " +//摄像机名称 + "manufacturer, " + //生产厂家 + "device_model, " + //设备型号 + "station_id, " + //变电站地址 + "ipaddr," + //'以太网IP地址', + "heart_beat_interval," + //'心跳间隔', + "datastatus," + + "lastmodifier," + + "lastmodifydate," + + "type," + + "online" + + ") VALUES (" + + "#{id}," + + "#{deviceId}," + + "#{deviceId}," + + "#{name}," + + "#{manufacturer}," + + "#{model}," + + "#{stationId}," + + "#{ip}," + + "#{keepaliveIntervalTime}," + + "'1'," + + "'注册创建'," + + "#{createTime}," + + "10," + + "1" + + ")") + int addRiisPatrolDevice(Device device); + + @Insert("INSERT INTO iis_substation (" + + "patroldevice_id, " + //UUID + "patroldevice_code, " +//摄像机编号 与 视频摄像头编号一致 + "patroldevice_name, " +//摄像机名称 + "manufacturer, " + //生产厂家 + "device_model, " + //设备型号 + "station_id, " + //变电站地址 + "ipaddr," + //'以太网IP地址', + "heart_beat_interval," + //'心跳间隔', + "datastatus," + + "lastmodifier," + + "lastmodifydate," + + "type," + + "online" + + ") VALUES (" + + "#{id}," + + "#{deviceId}," + + "#{name}," + + "#{manufacturer}," + + "#{model}," + + "#{stationId}," + + "#{ip}," + + "#{keepaliveIntervalTime}," + + "'1'," + + "'注册创建'," + + "#{createTime}," + + "10," + + "1" + + ")") + int addRiisStationNode(Device device); + + @Update(value = {" "}) + int update(Device device); + + @Update(value = {" "}) + int updateRiisPatrolDevice(Device device); + + @Update(value = {" "}) + int updateRiisSubstation(Device device); + + + @Select( + " " + ) + List getDevices(Boolean online); + + @Delete("DELETE FROM mon_device WHERE deviceId=#{deviceId}") + int del(String deviceId); + + @Select("SELECT " + + "deviceId, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "asMessageChannel," + + "geoCoordSys," + + "treeType," + + "online " + + " FROM mon_device WHERE online = 1") + List getOnlineDevices(); + @Select("SELECT " + + "deviceId, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "asMessageChannel," + + "geoCoordSys," + + "treeType," + + "online" + + " FROM mon_device WHERE ip = #{host} AND port=#{port}") + Device getDeviceByHostAndPort(String host, int port); + + @Update(value = {" "}) + void updateCustom(Device device); + + @Insert("INSERT INTO mon_device (" + + "deviceId, " + + "custom_name, " + + "password, " + + "sdpIp, " + + "createTime," + + "updateTime," + + "charset," + + "ssrcCheck," + + "asMessageChannel," + + "geoCoordSys," + + "treeType," + + "online," + + "mediaServerId" + + ") VALUES (" + + "#{deviceId}," + + "#{name}," + + "#{password}," + + "#{sdpIp}," + + "#{createTime}," + + "#{updateTime}," + + "#{charset}," + + "#{ssrcCheck}," + + "#{asMessageChannel}," + + "#{geoCoordSys}," + + "#{treeType}," + + "#{online}," + + "#{mediaServerId}" + + ")") + void addCustomDevice(Device device); + + @Select("select count(1) as total, sum(online) as online from mon_device") + ResourceBaceInfo getOverview(); + + @Select("select * from mon_device") + List getAll(); + + @Select("select * from mon_device where asMessageChannel = 1") + List queryDeviceWithAsMessageChannel(); + + @Update(value = {""}) + int updatePatroldeviceTime(String id, String preset); + + @Select("SELECT " + + "md.deviceId, " + + "coalesce(md.custom_name, md.name) as name, " + + "md.password, " + + "md.manufacturer, " + + "md.model, " + + "md.firmware, " + + "md.transport," + + "md.streamMode," + + "md.ip," + + "md.sdpIp," + + "md.localIp," + + "md.port," + + "md.hostAddress," + + "md.expires," + + "md.registerTime," + + "md.keepaliveTime," + + "md.createTime," + + "md.updateTime," + + "md.charset," + + "md.subscribeCycleForCatalog," + + "md.subscribeCycleForMobilePosition," + + "md.mobilePositionSubmissionInterval," + + "md.subscribeCycleForAlarm," + + "md.ssrcCheck," + + "md.asMessageChannel," + + "md.geoCoordSys," + + "md.treeType," + + "md.online," + + "md.mediaServerId," + + "(SELECT count(0) FROM mon_device_channel WHERE deviceId=md.deviceId) as channelCount "+ + " FROM mon_device md INNER JOIN mon_device_channel dc on dc.deviceId=md.deviceId WHERE dc.deviceId = #{deviceId} and dc.channelId= #{channelId}") + Device getDeviceByChannel(String deviceId, String channelId); + + @Select("SELECT " + + "patroldevice_id, " + + "patroldevice_code, " + + "rtspurl, " + + "ipaddr, " + + "channelinfo " + + "type " + + " FROM iis_substation_patroldevice WHERE video_mode ='1' and robots_code=#{robotCode}") + List getRiisPatrolDeviceByRobot(String robotCode); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceMobilePositionMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceMobilePositionMapper.java new file mode 100644 index 0000000..895a61b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/DeviceMobilePositionMapper.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.MobilePosition; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +@Mapper +public interface DeviceMobilePositionMapper { + + @Insert("INSERT INTO mon_device_mobile_position (deviceId,channelId, deviceName, time, longitude, latitude, altitude, speed, direction, reportSource, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime) " + + "VALUES (#{deviceId}, #{channelId}, #{deviceName}, #{time}, #{longitude}, #{latitude}, #{altitude}, #{speed}, #{direction}, #{reportSource}, #{longitudeGcj02}, #{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{createTime})") + int insertNewPosition(MobilePosition mobilePosition); + + @Select(value = {" "}) + List queryPositionByDeviceIdAndTime(String deviceId, String channelId, String startTime, String endTime); + + @Select("SELECT * FROM mon_device_mobile_position WHERE deviceId = #{deviceId}" + + " ORDER BY time DESC LIMIT 1") + MobilePosition queryLatestPositionByDevice(String deviceId); + + @Delete("DELETE FROM mon_device_mobile_position WHERE deviceId = #{deviceId}") + int clearMobilePositionsByDeviceId(String deviceId); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/GbStreamMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/GbStreamMapper.java new file mode 100644 index 0000000..7bd037e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/GbStreamMapper.java @@ -0,0 +1,172 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface GbStreamMapper { + + @Insert("REPLACE INTO mon_gb_stream (app, stream, gbId, name, " + + "longitude, latitude, streamType, mediaServerId, createTime) VALUES" + + "(#{app}, #{stream}, #{gbId}, #{name}, " + + "#{longitude}, #{latitude}, #{streamType}, " + + "#{mediaServerId}, #{createTime})") + @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") + int add(GbStream gbStream); + + @Update("UPDATE mon_gb_stream " + + "SET app=#{app}," + + "stream=#{stream}," + + "gbId=#{gbId}," + + "name=#{name}," + + "streamType=#{streamType}," + + "longitude=#{longitude}, " + + "latitude=#{latitude}," + + "mediaServerId=#{mediaServerId}" + + "WHERE app=#{app} AND stream=#{stream}") + int updateByAppAndStream(GbStream gbStream); + + @Update("UPDATE mon_gb_stream " + + "SET app=#{app}," + + "stream=#{stream}," + + "gbId=#{gbId}," + + "name=#{name}," + + "streamType=#{streamType}," + + "longitude=#{longitude}, " + + "latitude=#{latitude}," + + "mediaServerId=#{mediaServerId}" + + "WHERE gbStreamId=#{gbStreamId}") + int update(GbStream gbStream); + + @Delete("DELETE FROM mon_gb_stream WHERE app=#{app} AND stream=#{stream}") + int del(String app, String stream); + + @Select("") + List selectAll(String platformId, String catalogId, String query, String mediaServerId); + + @Select("SELECT * FROM mon_gb_stream WHERE app=#{app} AND stream=#{stream}") + GbStream selectOne(String app, String stream); + + @Select("SELECT * FROM mon_gb_stream WHERE gbId=#{gbId}") + List selectByGBId(String gbId); + + @Select("SELECT gs.*, pgs.platformId as platformId, pgs.catalogId as catalogId FROM mon_gb_stream gs " + + "LEFT JOIN mon_platform_gb_stream pgs ON gs.gbStreamId = pgs.gbStreamId " + + "WHERE gs.gbId = #{gbId} AND pgs.platformId = #{platformId}") + GbStream queryStreamInPlatform(String platformId, String gbId); + + @Select("") + List queryGbStreamListInPlatform(String platformId, boolean usPushingAsStatus); + + + @Select("SELECT gs.* FROM mon_gb_stream gs LEFT JOIN mon_platform_gb_stream pgs " + + "ON gs.gbStreamId = pgs.gbStreamId WHERE pgs.gbStreamId is NULL") + List queryStreamNotInPlatform(); + + @Delete("DELETE FROM mon_gb_stream WHERE streamType=#{type} AND gbId=NULL AND mediaServerId=#{mediaServerId}") + void deleteWithoutGBId(String type, String mediaServerId); + + @Delete("") + void batchDel(List streamProxyItemList); + + @Delete("") + void batchDelForGbStream(List gbStreams); + + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") + void batchAdd(List subList); + + @Update({""}) + int updateStreamGPS(List gpsMsgInfos); + + @Select("") + List selectAllForAppAndStream(List streamPushItems); + + @Update("UPDATE mon_gb_stream " + + "SET mediaServerId=#{mediaServerId}" + + "WHERE app=#{app} AND stream=#{stream}") + void updateMediaServer(String app, String stream, String mediaServerId); + + @Update("") + int updateGbIdOrName(List streamPushItemForUpdate); + + @Select("SELECT status FROM mon_stream_proxy WHERE app=#{app} AND stream=#{stream}") + Boolean selectStatusForProxy(String app, String stream); + + @Select("SELECT status FROM mon_stream_push WHERE app=#{app} AND stream=#{stream}") + Boolean selectStatusForPush(String app, String stream); + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/LogMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/LogMapper.java new file mode 100644 index 0000000..0a4a39b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/LogMapper.java @@ -0,0 +1,37 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import com.yfd.monitor.storager.dao.dto.LogDto; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储设服务的日志 + */ +@Mapper +@Repository +public interface LogMapper { + + @Insert("insert into mon_log ( name, type, uri, address, result, timing, username, createTime) " + + "values (#{name}, #{type}, #{uri}, #{address}, #{result}, #{timing}, #{username}, #{createTime})") + int add(LogDto logDto); + + @Select(value = {""}) + List query(String query, String type, String startTime, String endTime); + + @Delete("DELETE FROM log") + int clear(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/MediaServerMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/MediaServerMapper.java new file mode 100644 index 0000000..c96000b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/MediaServerMapper.java @@ -0,0 +1,131 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface MediaServerMapper { + + @Insert("INSERT INTO mon_media_server (" + + "id, " + + "ip, " + + "hookIp, " + + "sdpIp, " + + "streamIp, " + + "httpPort, " + + "httpSSlPort, " + + "rtmpPort, " + + "rtmpSSlPort, " + + "rtpProxyPort, " + + "rtspPort, " + + "rtspSSLPort, " + + "autoConfig, " + + "secret, " + + "rtpEnable, " + + "rtpPortRange, " + + "recordAssistPort, " + + "defaultServer, " + + "createTime, " + + "updateTime, " + + "hookAliveInterval" + + ") VALUES " + + "(" + + "#{id}, " + + "#{ip}, " + + "#{hookIp}, " + + "#{sdpIp}, " + + "#{streamIp}, " + + "#{httpPort}, " + + "#{httpSSlPort}, " + + "#{rtmpPort}, " + + "#{rtmpSSlPort}, " + + "#{rtpProxyPort}, " + + "#{rtspPort}, " + + "#{rtspSSLPort}, " + + "#{autoConfig}, " + + "#{secret}, " + + "#{rtpEnable}, " + + "#{rtpPortRange}, " + + "#{recordAssistPort}, " + + "#{defaultServer}, " + + "#{createTime}, " + + "#{updateTime}, " + + "#{hookAliveInterval})") + int add(MediaServerItem mediaServerItem); + + @Update(value = {" "}) + int update(MediaServerItem mediaServerItem); + + @Update(value = {" "}) + int updateByHostAndPort(MediaServerItem mediaServerItem); + + @Select("SELECT * FROM mon_media_server WHERE id=#{id}") + MediaServerItem queryOne(String id); + + @Select("SELECT * FROM mon_media_server") + List queryAll(); + + @Delete("DELETE FROM mon_media_server WHERE id=#{id}") + void delOne(String id); + + @Select("DELETE FROM mon_media_server WHERE ip=#{host} and httpPort=#{port}") + void delOneByIPAndPort(String host, int port); + + @Delete("DELETE FROM mon_media_server WHERE defaultServer=1") + int delDefault(); + + @Select("SELECT * FROM mon_media_server WHERE ip=#{host} and httpPort=#{port}") + MediaServerItem queryOneByHostAndPort(String host, int port); + + @Select("SELECT * FROM mon_media_server WHERE defaultServer=1") + MediaServerItem queryDefault(); + + @Select("SELECT * FROM wvp_media_server WHERE record_assist_port > 0") + List queryAllWithAssistPort(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/ParentPlatformMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/ParentPlatformMapper.java new file mode 100644 index 0000000..9abc740 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/ParentPlatformMapper.java @@ -0,0 +1,101 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.storager.dao.dto.ChannelSourceInfo; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * 用于存储上级平台 + */ +@Mapper +@Repository +public interface ParentPlatformMapper { + + @Insert("INSERT INTO mon_parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " + + " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, asMessageChannel, " + + " status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " + + " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " + + " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " + + " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})") + int addParentPlatform(ParentPlatform parentPlatform); + + @Update("UPDATE mon_parent_platform " + + "SET enable=#{enable}, " + + "name=#{name}," + + "deviceGBId=#{deviceGBId}," + + "serverGBId=#{serverGBId}, " + + "serverGBDomain=#{serverGBDomain}, " + + "serverIP=#{serverIP}," + + "serverPort=#{serverPort}, " + + "deviceIp=#{deviceIp}, " + + "devicePort=#{devicePort}, " + + "username=#{username}, " + + "password=#{password}, " + + "expires=#{expires}, " + + "keepTimeout=#{keepTimeout}, " + + "transport=#{transport}, " + + "characterSet=#{characterSet}, " + + "ptz=#{ptz}, " + + "rtcp=#{rtcp}, " + + "asMessageChannel=#{asMessageChannel}, " + + "status=#{status}, " + + "startOfflinePush=#{startOfflinePush}, " + + "catalogGroup=#{catalogGroup}, " + + "administrativeDivision=#{administrativeDivision}, " + + "createTime=#{createTime}, " + + "updateTime=#{updateTime}, " + + "treeType=#{treeType}, " + + "catalogId=#{catalogId} " + + "WHERE id=#{id}") + int updateParentPlatform(ParentPlatform parentPlatform); + + @Delete("DELETE FROM mon_parent_platform WHERE serverGBId=#{serverGBId}") + int delParentPlatform(ParentPlatform parentPlatform); + + @Select("SELECT *, ((SELECT count(0)\n" + + " FROM mon_platform_gb_channel pc\n" + + " WHERE pc.platformId = pp.serverGBId)\n" + + " +\n" + + " (SELECT count(0)\n" + + " FROM mon_platform_gb_stream pgs\n" + + " WHERE pgs.platformId = pp.serverGBId)\n" + + " +\n" + + " (SELECT count(0)\n" + + " FROM mon_platform_catalog pgc\n" + + " WHERE pgc.platformId = pp.serverGBId)) as channelCount\n" + + "FROM mon_parent_platform pp ") + List getParentPlatformList(); + + @Select("SELECT * FROM mon_parent_platform WHERE enable=#{enable} ") + List getEnableParentPlatformList(boolean enable); + + @Select("SELECT * FROM mon_parent_platform WHERE enable=1 and asMessageChannel = 1") + List queryEnablePlatformListWithAsMessageChannel(); + + @Select("SELECT * FROM mon_parent_platform WHERE serverGBId=#{platformGbId}") + ParentPlatform getParentPlatByServerGBId(String platformGbId); + + @Select("SELECT * FROM mon_parent_platform WHERE id=#{id}") + ParentPlatform getParentPlatById(int id); + + @Update("UPDATE mon_parent_platform SET status=false" ) + int outlineForAllParentPlatform(); + + @Update("UPDATE mon_parent_platform SET status=#{online} WHERE serverGBId=#{platformGbID}" ) + int updateParentPlatformStatus(String platformGbID, boolean online); + + @Update(value = {" "}) + int setDefaultCatalog(String platformId, String catalogId, String updateTime); + + @Select("select 'channel' as name, count(pgc.platformId) count from mon_platform_gb_channel pgc left join mon_device_channel dc on dc.id = pgc.deviceChannelId where pgc.platformId=#{platformId} and dc.channelId =#{gbId} " + + "union " + + "select 'stream' as name, count(pgs.platformId) count from mon_platform_gb_stream pgs left join mon_gb_stream gs on pgs.gbStreamId = gs.gbStreamId where pgs.platformId=#{platformId} and gs.gbId = #{gbId}") + List getChannelSource(String platformId, String gbId); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformCatalogMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformCatalogMapper.java new file mode 100644 index 0000000..865db82 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformCatalogMapper.java @@ -0,0 +1,62 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.gdw2019.bean.PlatformCatalog; +import com.yfd.monitor.gdw2019.bean.PlatformGbStream; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface PlatformCatalogMapper { + + @Insert("INSERT INTO mon_platform_catalog (id, name, platformId, parentId, civilCode, businessGroupId) VALUES" + + "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})") + int add(PlatformCatalog platformCatalog); + + @Delete("DELETE FROM mon_platform_catalog WHERE id=#{id}") + int del(String id); + + @Delete("DELETE FROM mon_platform_catalog WHERE platformId=#{platformId}") + int delByPlatformId(String platformId); + + @Select("SELECT pc.*, count(pc2.id) as childrenCount FROM mon_platform_catalog pc " + + "left join mon_platform_catalog pc2 on pc.id = pc2.parentId " + + "WHERE pc.parentId=#{parentId} AND pc.platformId=#{platformId} group by pc.id") + List selectByParentId(String platformId, String parentId); + + @Select("SELECT *, (SELECT COUNT(1) from mon_platform_catalog where parentId = pc.id) as childrenCount FROM mon_platform_catalog pc WHERE pc.id=#{id}") + PlatformCatalog select(String id); + + @Update(value = {" "}) + int update(PlatformCatalog platformCatalog); + + @Select("SELECT *, (SELECT COUNT(1) from mon_platform_catalog where parentId = pc.id) as childrenCount FROM mon_platform_catalog pc WHERE pc.platformId=#{platformId}") + List selectByPlatForm(String platformId); + + @Select("SELECT pc.* FROM mon_platform_catalog pc WHERE pc.id = (SELECT pp.catalogId from mon_parent_platform pp WHERE pp.serverGBId=#{platformId})") + PlatformCatalog selectDefaultByPlatFormId(String platformId); + + + @Select("SELECT pc.* FROM mon_platform_catalog pc WHERE pc.id = #{id}") + PlatformCatalog selectParentCatalog(String id); + + @Select("SELECT pc.id as channelId, pc.name, pc.civilCode, pc.businessGroupId,'1' as parental, pc.parentId " + + " FROM mon_platform_catalog pc WHERE pc.platformId=#{platformId}") + List queryCatalogInPlatform(String platformId); + + @Select("SELECT *, " + + "(SELECT COUNT(1) from mon_platform_catalog where parent_id = pc.id) as children_count " + + " from mon_platform_catalog pc " + + " WHERE pc.id=#{id} and pc.platform_id=#{platformId}") + PlatformCatalog selectByPlatFormAndCatalogId(@Param("platformId") String platformId, @Param("id") String id); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformChannelMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformChannelMapper.java new file mode 100644 index 0000000..64566fb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformChannelMapper.java @@ -0,0 +1,121 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.PlatformCatalog; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface PlatformChannelMapper { + + /** + * 查询列表里已经关联的 + */ + @Select("") + List findChannelRelatedPlatform(String platformId, List channelReduces); + + @Insert("") + int addChannels(String platformId, List channelReducesToAdd); + + @Delete("") + int delChannelForGB(String platformId, List channelReducesToDel); + + @Delete("") + int delChannelForDeviceId(String deviceId); + + @Delete("") + int cleanChannelForGB(String platformId); + + @Select("SELECT dc.* FROM mon_platform_gb_channel pgc left join mon_device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId=#{channelId} and pgc.platformId=#{platformId}") + List queryChannelInParentPlatform(String platformId, String channelId); + + @Select("SELECT dc.* FROM mon_platform_gb_channel pgc left join mon_device_channel dc on dc.id = pgc.deviceChannelId WHERE pgc.platformId=#{platformId} and pgc.catalogId=#{catalogId}") + List queryAllChannelInCatalog(String platformId, String catalogId); + + @Select(" select dc.channelId as id, dc.name as name, pgc.platformId as platformId, pgc.catalogId as parentId, 0 as childrenCount, 1 as type " + + " from mon_device_channel dc left join mon_platform_gb_channel pgc on dc.id = pgc.deviceChannelId " + + " where pgc.platformId=#{platformId} and pgc.catalogId=#{catalogId}") + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + + @Select("select d.*\n" + + "from mon_platform_gb_channel pgc\n" + + " left join mon_device_channel dc on dc.id = pgc.deviceChannelId\n" + + " left join mon_device d on dc.deviceId = d.deviceId\n" + + " inner join iis_substation_patroldevice sp on sp.internationalId = d.deviceId\n" + + "where sp.patroldevice_code = #{channelId} and pgc.platformId=#{platformId}") + List queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); + + @Delete("") + int delByCatalogId(String id); + + @Delete("") + int delByCatalogIdAndChannelIdAndPlatformId(PlatformCatalog platformCatalog); + + @Select(" ") + List queryPlatFormListForGBWithGBId(String channelId, List platforms); + + @Delete("") + void delByPlatformId(String serverGBId); + + @Delete("") + int delChannelForGBByCatalogId(String platformId, String catalogId); + + @Select("select dc.channelId deviceId,dc.name,d.manufacturer,d.model,d.firmware\n" + + "from pmon_latform_gb_channel pgc\n" + + " left join mon_device_channel dc on dc.id = pgc.deviceChannelId\n" + + " left join mon_device d on dc.deviceId = d.deviceId\n" + + "where dc.channelId = #{channelId} and pgc.platformId=#{platformId}") + List queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId); + + @Select("SELECT pgc.platformId FROM mon_platform_gb_channel pgc left join mon_device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId='${channelId}'") + List queryParentPlatformByChannelId(String channelId); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformGbStreamMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformGbStreamMapper.java new file mode 100644 index 0000000..19ec53c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/PlatformGbStreamMapper.java @@ -0,0 +1,111 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.PlatformCatalog; +import com.yfd.monitor.gdw2019.bean.PlatformGbStream; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface PlatformGbStreamMapper { + + @Insert("REPLACE INTO mon_platform_gb_stream (gbStreamId, platformId, catalogId) VALUES" + + "( #{gbStreamId}, #{platformId}, #{catalogId})") + int add(PlatformGbStream platformGbStream); + + + @Insert("") + int batchAdd(List streamPushItems); + + @Delete("DELETE FROM mon_platform_gb_stream WHERE gbStreamId = (select gbStreamId from mon_gb_stream where app=#{app} AND stream=#{stream})") + int delByAppAndStream(String app, String stream); + + @Delete("DELETE FROM mon_platform_gb_stream WHERE platformId=#{platformId}") + int delByPlatformId(String platformId); + + @Select("SELECT " + + "pp.* " + + "FROM " + + "mon_platform_gb_stream pgs " + + "LEFT JOIN mon_parent_platform pp ON pp.serverGBId = pgs.platformId " + + "LEFT JOIN mon_gb_stream gs ON gs.gbStreamId = pgs.gbStreamId " + + "WHERE " + + "gs.app =#{app} " + + "AND gs.stream =#{stream} ") + List selectByAppAndStream(String app, String stream); + + @Select("SELECT pgs.*, gs.gbId FROM mon_platform_gb_stream pgs " + + "LEFT JOIN mon_gb_stream gs ON pgs.gbStreamId = gs.gbStreamId " + + "WHERE gs.app=#{app} AND gs.stream=#{stream} AND pgs.platformId=#{serverGBId}") + StreamProxyItem selectOne(String app, String stream, String serverGBId); + + @Select("select gs.* \n" + + "from mon_gb_stream gs\n" + + " left join mon_platform_gb_stream pgs\n" + + " on gs.gbStreamId = pgs.gbStreamId\n" + + "where pgs.platformId=#{platformId} and pgs.catalogId=#{catalogId}") + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + + @Select("select gs.gbId as id, gs.name as name, pgs.platformId as platformId, pgs.catalogId as catalogId , 0 as childrenCount, 2 as type\n" + + "from mon_gb_stream gs\n" + + " left join mon_platform_gb_stream pgs\n" + + " on gs.gbStreamId = pgs.gbStreamId\n" + + "where pgs.platformId=#{platformId} and pgs.catalogId=#{catalogId}") + List queryChannelInParentPlatformAndCatalogForCatalog(String platformId, String catalogId); + + @Delete("DELETE FROM platform_gb_stream WHERE catalogId=#{id}") + int delByCatalogId(String id); + + @Select(" ") + List queryPlatFormListForGBWithGBId(String app, String stream, List platforms); + + @Delete("DELETE FROM mon_platform_gb_stream WHERE gbStreamId = (select id from mon_gb_stream where app=#{app} AND stream=#{stream}) AND platformId=#{platformId}") + int delByAppAndStreamAndPlatform(String app, String stream, String platformId); + + @Delete("") + void delByGbStreams(List gbStreams); + + @Delete("") + void delByAppAndStreamsByPlatformId(List gbStreams, String platformId); + + @Delete("DELETE FROM mon_platform_gb_stream WHERE platformId=#{platformId} and catalogId=#{catalogId}") + int delByPlatformAndCatalogId(String platformId, String catalogId); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/RecordInfoDao.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/RecordInfoDao.java new file mode 100644 index 0000000..3f17f96 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/RecordInfoDao.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.storager.dao.dto.RecordInfo; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface RecordInfoDao { + + @Insert("INSERT INTO recordInfo (app, stream, mediaServerId, createTime, type, deviceId, channelId, name) VALUES" + + "(#{app}, #{stream}, #{mediaServerId}, datetime('now','localtime')), #{type}, #{deviceId}, #{channelId}, #{name}") + int add(RecordInfo recordInfo); + + @Delete("DELETE FROM user WHERE createTime < #{beforeTime}") + int deleteBefore(String beforeTime); + + @Select("select * FROM recordInfo") + List selectAll(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/RoleMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/RoleMapper.java new file mode 100644 index 0000000..db0e774 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/RoleMapper.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.storager.dao.dto.Role; +import com.yfd.monitor.storager.dao.dto.User; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface RoleMapper { + + @Insert("INSERT INTO mon_user_role (name, authority, createTime, updateTime) VALUES" + + "(#{name}, #{authority}, #{createTime}, #{updateTime})") + int add(Role role); + + @Update(value = {" "}) + int update(Role role); + + @Delete("DELETE FROM mon_user_role WHERE id != 1 and id=#{id}") + int delete(int id); + + @Select("select * FROM mon_user_role WHERE id=#{id}") + Role selectById(int id); + + @Select("select * FROM mon_user_role") + List selectAll(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/StreamProxyMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/StreamProxyMapper.java new file mode 100644 index 0000000..6083186 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/StreamProxyMapper.java @@ -0,0 +1,82 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface StreamProxyMapper { + + @Insert("INSERT INTO mon_stream_proxy (type, name, app, stream,mediaServerId, url, src_url, dst_url, " + + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, createTime) VALUES" + + "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{src_url}, #{dst_url}, " + + "#{timeout_ms}, #{ffmpeg_cmd_key}, #{rtp_type}, #{enable_audio}, #{enable_mp4}, #{enable}, #{status}, " + + "#{enable_remove_none_reader}, #{enable_disable_none_reader}, #{createTime} )") + int add(StreamProxyItem streamProxyDto); + + @Update("UPDATE mon_stream_proxy " + + "SET type=#{type}, " + + "name=#{name}," + + "app=#{app}," + + "stream=#{stream}," + + "url=#{url}, " + + "mediaServerId=#{mediaServerId}, " + + "src_url=#{src_url}," + + "dst_url=#{dst_url}, " + + "timeout_ms=#{timeout_ms}, " + + "ffmpeg_cmd_key=#{ffmpeg_cmd_key}, " + + "rtp_type=#{rtp_type}, " + + "enable_audio=#{enable_audio}, " + + "enable=#{enable}, " + + "status=#{status}, " + + "enable_remove_none_reader=#{enable_remove_none_reader}, " + + "enable_disable_none_reader=#{enable_disable_none_reader}, " + + "enable_mp4=#{enable_mp4} " + + "WHERE app=#{app} AND stream=#{stream}") + int update(StreamProxyItem streamProxyDto); + + @Delete("DELETE FROM mon_stream_proxy WHERE app=#{app} AND stream=#{stream}") + int del(String app, String stream); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM mon_stream_proxy st LEFT JOIN mon_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.createTime desc") + List selectAll(); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM mon_stream_proxy st LEFT JOIN mon_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=#{enable} order by st.createTime desc") + List selectForEnable(boolean enable); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM mon_stream_proxy st LEFT JOIN mon_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.createTime desc") + StreamProxyItem selectOne(String app, String stream); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM mon_stream_proxy st " + + "LEFT JOIN mon_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + + "WHERE st.enable=#{enable} and st.mediaServerId = #{id} order by st.createTime desc") + List selectForEnableInMediaServer(String id, boolean enable); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM mon_stream_proxy st " + + "LEFT JOIN mon_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + + "WHERE st.mediaServerId = #{id} order by st.createTime desc") + List selectInMediaServer(String id); + + @Update("UPDATE mon_stream_proxy " + + "SET status=#{status} " + + "WHERE mediaServerId=#{mediaServerId}") + void updateStatusByMediaServerId(String mediaServerId, boolean status); + + @Update("UPDATE mon_stream_proxy " + + "SET status=#{status} " + + "WHERE app=#{app} AND stream=#{stream}") + int updateStatus(String app, String stream, boolean status); + + @Delete("DELETE FROM mon_stream_proxy WHERE enable_remove_none_reader=true AND mediaServerId=#{mediaServerId}") + void deleteAutoRemoveItemByMediaServerId(String mediaServerId); + + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM mon_stream_proxy st LEFT JOIN mon_gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable_remove_none_reader=true AND st.mediaServerId=#{mediaServerId} order by st.createTime desc") + List selecAutoRemoveItemByMediaServerId(String mediaServerId); + + @Select("select count(1) as total, sum(status) as online from mon_stream_proxy") + ResourceBaceInfo getOverview(); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/StreamPushMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/StreamPushMapper.java new file mode 100644 index 0000000..16da377 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/StreamPushMapper.java @@ -0,0 +1,181 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.bean.StreamPushItemFromRedis; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import org.apache.ibatis.annotations.*; +// import org.omg.PortableInterceptor.INACTIVE; +import org.springframework.stereotype.Repository; + +import java.util.Collection; +import java.util.List; + +@Mapper +@Repository +public interface StreamPushMapper { + + @Insert("INSERT INTO mon_stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + + "pushTime, aliveSecond, mediaServerId, serverId, updateTime, createTime, pushIng, self) VALUES" + + "(#{app}, #{stream}, #{totalReaderCount}, #{originType}, #{originTypeStr}, " + + "#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{serverId} , #{updateTime} , #{createTime}, " + + "#{pushIng}, #{self} )") + int add(StreamPushItem streamPushItem); + + + @Update(value = {" "}) + int update(StreamPushItem streamPushItem); + + @Delete("DELETE FROM mon_stream_push WHERE app=#{app} AND stream=#{stream}") + int del(String app, String stream); + + @Delete("") + int delAllWithoutGBId(List streamPushItems); + + @Delete("") + int delAll(List streamPushItems); + + @Delete("") + int delAllForGbStream(List gbStreams); + + + @Select(value = {" "}) + List selectAllForList(String query, Boolean pushing, String mediaServerId); + + @Select("SELECT st.*, gs.gbId, gs.name, gs.longitude, gs.latitude FROM mon_stream_push st LEFT JOIN mon_gb_stream gs on st.app = gs.app AND st.stream = gs.stream order by st.createTime desc") + List selectAll(); + + @Select("SELECT st.*, gs.gbId, gs.name, gs.longitude, gs.latitude FROM mon_stream_push st LEFT JOIN mon_gb_stream gs on st.app = gs.app AND st.stream = gs.stream WHERE st.app=#{app} AND st.stream=#{stream}") + StreamPushItem selectOne(String app, String stream); + + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int addAll(List streamPushItems); + + @Delete("DELETE FROM mon_stream_push") + void clear(); + + @Delete("DELETE sp FROM mon_stream_push sp left join mon_gb_stream gs on gs.app = sp.app and gs.stream= sp.stream WHERE sp.mediaServerId=#{mediaServerId} and gs.gbId is null ") + void deleteWithoutGBId(String mediaServerId); + + @Select("SELECT * FROM mon_stream_push WHERE mediaServerId=#{mediaServerId}") + List selectAllByMediaServerId(String mediaServerId); + + @Select("SELECT sp.* FROM mon_stream_push sp left join mon_gb_stream gs on gs.app = sp.app and gs.stream= sp.stream WHERE sp.mediaServerId=#{mediaServerId} and gs.gbId is null") + List selectAllByMediaServerIdWithOutGbID(String mediaServerId); + + @Update("UPDATE mon_stream_push " + + "SET status=#{status} " + + "WHERE app=#{app} AND stream=#{stream}") + int updateStatus(String app, String stream, boolean status); + + @Update("UPDATE mon_stream_push " + + "SET pushIng=#{pushIng} " + + "WHERE app=#{app} AND stream=#{stream}") + int updatePushStatus(String app, String stream, boolean pushIng); + + @Update("UPDATE mon_stream_push " + + "SET status=#{status} " + + "WHERE mediaServerId=#{mediaServerId}") + void updateStatusByMediaServerId(String mediaServerId, boolean status); + + + @Select("") + List getOnlinePusherForGbInList(List offlineStreams); + + @Update("") + void offline(List offlineStreams); + + @Select("") + List getOfflinePusherForGbInList(List onlineStreams); + + @Update("") + void online(List onlineStreams); + + @Select("SELECT gs.* FROM mon_stream_push sp left join mon_gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream where sp.status = 1") + List getOnlinePusherForGb(); + + @Update("UPDATE mon_stream_push SET status=0") + void setAllStreamOffline(); + + @Select("SELECT CONCAT(app,stream) FROM mon_gb_stream") + List getAllAppAndStream(); + + @Select(value = {" "}) + ResourceBaceInfo getOverview(boolean pushIngAsOnline); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/UserMapper.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/UserMapper.java new file mode 100644 index 0000000..ea12a30 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/UserMapper.java @@ -0,0 +1,65 @@ +package com.yfd.monitor.storager.dao; + +import com.yfd.monitor.storager.dao.dto.User; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Mapper +@Repository +public interface UserMapper { + + @Insert("INSERT INTO mon_user (username, password, roleId, pushKey, createTime, updateTime) VALUES" + + "(#{username}, #{password}, #{role.id}, #{pushKey}, #{createTime}, #{updateTime})") + int add(User user); + + @Update(value = {" "}) + int update(User user); + + @Delete("DELETE FROM mon_user WHERE id != 1 and id=#{id}") + int delete(int id); + + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM mon_user u, mon_user_role r WHERE u.roleId=r.id and u.username=#{username} AND u.password=#{password}") + @Results(id = "roleMap", value = { + @Result(column = "roleID", property = "role.id"), + @Result(column = "roleName", property = "role.name"), + @Result(column = "roleAuthority", property = "role.authority"), + @Result(column = "roleCreateTime", property = "role.createTime"), + @Result(column = "roleUpdateTime", property = "role.updateTime") + }) + User select(String username, String password); + + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM mon_user u, mon_user_role r WHERE u.roleId=r.id and u.id=#{id}") + @ResultMap(value="roleMap") + User selectById(int id); + + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM mon_user u, mon_user_role r WHERE u.roleId=r.id and u.username=#{username}") + @ResultMap(value="roleMap") + User getUserByUsername(String username); + + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM mon_user u, mon_user_role r WHERE u.roleId=r.id") + @ResultMap(value="roleMap") + List selectAll(); + + @Select("select * from (select user.*, concat(concat(#{callId}, '_'), pushKey) as str1 from mon_user) as u where md5(u.str1) = #{sign}") + List checkPushAuthorityByCallIdAndSign(String callId, String sign); + + @Select("select * from mon_user where md5(pushKey) = #{sign}") + List checkPushAuthorityByCallId(String sign); + + @Select("select u.id, u.username,u.pushKey,u.roleId, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM mon_user u join mon_user_role r on u.roleId=r.id") + @ResultMap(value="roleMap") + List getUsers(); + + @Update("update mon_user set pushKey=#{pushKey} where id=#{id}") + int changePushKey(int id, String pushKey); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/ChannelSourceInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/ChannelSourceInfo.java new file mode 100644 index 0000000..ac78022 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/ChannelSourceInfo.java @@ -0,0 +1,22 @@ +package com.yfd.monitor.storager.dao.dto; + +public class ChannelSourceInfo { + private String name; + private int count; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/LogDto.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/LogDto.java new file mode 100644 index 0000000..dc5f7dd --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/LogDto.java @@ -0,0 +1,86 @@ +package com.yfd.monitor.storager.dao.dto; + +public class LogDto { + + private int id; + private String name; + private String type; + private String uri; + private String address; + private String result; + private long timing; + private String username; + private String createTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public long getTiming() { + return timing; + } + + public void setTiming(long timing) { + this.timing = timing; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/PlatformRegisterInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/PlatformRegisterInfo.java new file mode 100644 index 0000000..48c4c16 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/PlatformRegisterInfo.java @@ -0,0 +1,40 @@ +package com.yfd.monitor.storager.dao.dto; + +/** + * 平台发送注册/注销消息时缓存此消息 + */ +public class PlatformRegisterInfo { + + /** + * 平台Id + */ + private String platformId; + + /** + * 是否时注册,false为注销 + */ + private boolean register; + + public static PlatformRegisterInfo getInstance(String platformId, boolean register) { + PlatformRegisterInfo platformRegisterInfo = new PlatformRegisterInfo(); + platformRegisterInfo.setPlatformId(platformId); + platformRegisterInfo.setRegister(register); + return platformRegisterInfo; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public boolean isRegister() { + return register; + } + + public void setRegister(boolean register) { + this.register = register; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/RecordInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/RecordInfo.java new file mode 100644 index 0000000..40640c6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/RecordInfo.java @@ -0,0 +1,133 @@ +package com.yfd.monitor.storager.dao.dto; + +/** + * 录像记录 + */ +public class RecordInfo { + + /** + * ID + */ + private int id; + + /** + * 应用名 + */ + private String app; + + /** + * 流ID + */ + private String stream; + + /** + * 对应的zlm流媒体的ID + */ + private String mediaServerId; + + /** + * 创建时间 + */ + private String createTime; + + /** + * 类型 对应zlm的 originType + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7, + * rtc_push=8 + */ + private int type; + + /** + * 国标录像时的设备ID + */ + private String deviceId; + + /** + * 国标录像时的通道ID + */ + private String channelId; + + /** + * 拉流代理录像时的名称 + */ + private String name; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/Role.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/Role.java new file mode 100644 index 0000000..8f2dc0e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/Role.java @@ -0,0 +1,50 @@ +package com.yfd.monitor.storager.dao.dto; + +public class Role { + + private int id; + private String name; + private String authority; + private String createTime; + private String updateTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAuthority() { + return authority; + } + + public void setAuthority(String authority) { + this.authority = authority; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/User.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/User.java new file mode 100644 index 0000000..cdff7b1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/dao/dto/User.java @@ -0,0 +1,68 @@ +package com.yfd.monitor.storager.dao.dto; + +public class User { + + private int id; + private String username; + private String password; + private String createTime; + private String updateTime; + private String pushKey; + private Role role; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + + public String getPushKey() { + return pushKey; + } + + public void setPushKey(String pushKey) { + this.pushKey = pushKey; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/impl/RedisCatchStorageImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/impl/RedisCatchStorageImpl.java new file mode 100644 index 0000000..7f4b6c9 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/impl/RedisCatchStorageImpl.java @@ -0,0 +1,974 @@ +package com.yfd.monitor.storager.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.common.SystemAllInfo; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.media.bean.MediaInfo; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamAuthorityInfo; +import com.yfd.monitor.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.service.bean.MessageForPushChannel; +import com.yfd.monitor.service.bean.ThirdPartyGB; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.dao.DeviceChannelMapper; +import com.yfd.monitor.storager.dao.dto.PlatformRegisterInfo; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.utils.JsonUtil; +import com.yfd.monitor.utils.SystemInfoUtils; +import com.yfd.monitor.utils.redis.RedisUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.sip.header.WWWAuthenticateHeader; +import java.util.*; + +@SuppressWarnings("rawtypes") +@Component +public class RedisCatchStorageImpl implements IRedisCatchStorage { + + private final Logger logger = LoggerFactory.getLogger(RedisCatchStorageImpl.class); + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private UserSetting userSetting; + + @Autowired + private RedisTemplate redisTemplate; + + @Override + public Long getCSEQ() { + String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId(); + + Long result = redisTemplate.opsForValue().increment(key, 1L); + if (result != null && result > Integer.MAX_VALUE) { + redisTemplate.opsForValue().set(key, 1); + result = 1L; + } + return result; + } + + @Override + public Long getSN(String method) { + String key = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_" + method; + + Long result = redisTemplate.opsForValue().increment(key, 1L); + if (result != null && result > Integer.MAX_VALUE) { + redisTemplate.opsForValue().set(key, 1); + result = 1L; + } + return result; + } + + @Override + public void resetAllCSEQ() { + String scanKey = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + redisTemplate.opsForValue().set(key, 1); + } + } + + @Override + public void resetAllSN() { + String scanKey = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + redisTemplate.opsForValue().set(key, 1); + } + } + + /** + * 开始播放时将流存入redis + */ + @Override + public boolean startPlay(StreamInfo stream) { + + redisTemplate.opsForValue().set(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), + stream.getMediaServerId(), stream.getStream(), stream.getDeviceID(), stream.getChannelId()), + stream); + return true; + } + + /** + * 停止播放时从redis删除 + */ + @Override + public boolean stopPlay(StreamInfo streamInfo) { + if (streamInfo == null) { + return false; + } + Boolean result = redisTemplate.delete(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), + streamInfo.getMediaServerId(), + streamInfo.getStream(), + streamInfo.getDeviceID(), + streamInfo.getChannelId())); + return result != null && result; + } + + /** + * 查询播放列表 + */ + @Override + public StreamInfo queryPlay(StreamInfo streamInfo) { + return (StreamInfo)redisTemplate.opsForValue().get(String.format("%S_%s_%s_%s_%s_%s", + VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), + streamInfo.getMediaServerId(), + streamInfo.getStream(), + streamInfo.getDeviceID(), + streamInfo.getChannelId())); + } + @Override + public StreamInfo queryPlayByStreamId(String streamId) { + List playLeys = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), streamId)); + if (playLeys.size() == 0) { + return null; + } + return (StreamInfo)redisTemplate.opsForValue().get(playLeys.get(0).toString()); + } + + @Override + public StreamInfo queryPlayByDevice(String deviceId, String channelId) { + List playLeys = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), + deviceId, + channelId)); + if (playLeys.size() == 0) { + return null; + } + return (StreamInfo)redisTemplate.opsForValue().get(playLeys.get(0).toString()); + } + + @Override + public Map queryPlayByDeviceId(String deviceId) { + Map streamInfos = new HashMap<>(); + List players = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(),deviceId)); + if (players.size() == 0) { + return streamInfos; + } + for (Object player : players) { + String key = (String) player; + StreamInfo streamInfo = JsonUtil.redisJsonToObject(redisTemplate, key, StreamInfo.class); + if (Objects.isNull(streamInfo)) { + continue; + } + streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo); + } + return streamInfos; + } + + @Override + public MediaInfo getStreamInfo1(String app, String streamId, String mediaServerId) { + String scanKey = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_*_" + app + "_" + streamId + "_" + mediaServerId; + + MediaInfo result = null; + List keys = RedisUtil.scan(redisTemplate, scanKey); + if (keys.size() > 0) { + String key = (String) keys.get(0); + result = JsonUtil.redisJsonToObject(redisTemplate, key, MediaInfo.class); + } + + return result; + } + + @Override + public void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, MediaInfo mediaInfo) { + // 查找是否使用了callID + StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); + if (streamAuthorityInfo != null) { + mediaInfo.setCallId(streamAuthorityInfo.getCallId()); + } + redisTemplate.opsForValue().set(key, mediaInfo); + } + + @Override + public boolean startPlayback(StreamInfo stream, String callId) { + redisTemplate.opsForValue().set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream); + return true; + } + + @Override + public boolean startDownload(StreamInfo stream, String callId) { + String key=String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId); + if (stream.getProgress() == 1) { + logger.debug("添加下载缓存==已完成下载=》{}",key); + redisTemplate.opsForValue().set(key, stream); + }else { + logger.debug("添加下载缓存==未完成下载=》{}",key); + redisTemplate.opsForValue().set(key, stream, 60*60); + } + return true; + } + @Override + public boolean stopDownload(String deviceId, String channelId, String stream, String callId) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(null); + deviceChannel.setDeviceId(deviceId); + deviceChannelMapper.update(deviceChannel); + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List scan = RedisUtil.scan(redisTemplate, key); + if (scan.size() > 0) { + for (Object keyObj : scan) { + redisTemplate.delete(keyObj); + } + } + return true; + } + + @Override + public boolean stopPlayback(String deviceId, String channelId, String stream, String callId) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(null); + deviceChannel.setDeviceId(deviceId); + deviceChannelMapper.update(deviceChannel); + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List scan = RedisUtil.scan(redisTemplate, key); + if (scan.size() > 0) { + for (Object keyObj : scan) { + redisTemplate.delete(keyObj); + } + } + return true; + } + + @Override + public StreamInfo queryPlayback(String deviceId, String channelId, String stream, String callId) { + if (stream == null && callId == null) { + return null; + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List streamInfoScan = RedisUtil.scan(redisTemplate, key); + if (streamInfoScan.size() > 0) { + return (StreamInfo) redisTemplate.opsForValue().get(streamInfoScan.get(0)); + }else { + return null; + } + } + + @Override + public String queryPlaybackForKey(String deviceId, String channelId, String stream, String callId) { + if (stream == null && callId == null) { + return null; + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List streamInfoScan = RedisUtil.scan(redisTemplate, key); + return (String) streamInfoScan.get(0); + } + + @Override + public void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch) { + String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + parentPlatformCatch.getId(); + redisTemplate.opsForValue().set(key, parentPlatformCatch); + } + + @Override + public ParentPlatformCatch queryPlatformCatchInfo(String platformGbId) { + return (ParentPlatformCatch)redisTemplate.opsForValue().get(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + @Override + public void updatePlatformWWwInfo(String platformGbId,cn.hutool.json.JSONObject www) { + String key = VideoManagerConstants.PLATFORM_WWW_PREFIX + userSetting.getServerId() + "_" + platformGbId; + redisTemplate.opsForValue().set(key, www); + } + + @Override + public Object queryPlatformWWwInfo(String platformGbId) { + String key=VideoManagerConstants.PLATFORM_WWW_PREFIX + userSetting.getServerId() + "_" + platformGbId; + return redisTemplate.opsForValue().get(key); + } + + @Override + public void delPlatformCatchInfo(String platformGbId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + + + @Override + public void delPlatformKeepalive(String platformGbId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + @Override + public void delPlatformRegister(String platformGbId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetting.getServerId() + "_" + platformGbId); + } + + + @Override + public void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo) { + String key = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId; + redisTemplate.opsForValue().set(key, platformRegisterInfo, 30); + } + + + @Override + public PlatformRegisterInfo queryPlatformRegisterInfo(String callId) { + return (PlatformRegisterInfo)redisTemplate.opsForValue().get(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); + } + + @Override + public void delPlatformRegisterInfo(String callId) { + redisTemplate.delete(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); + } + + @Override + public void cleanPlatformRegisterInfos() { + List regInfos = RedisUtil.scan(redisTemplate, VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + "*"); + for (Object key : regInfos) { + redisTemplate.delete(key.toString()); + } + } + + @Override + public void updateSendRTPSever(SendRtpItem sendRtpItem) { + + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_" + + sendRtpItem.getMediaServerId() + "_" + + sendRtpItem.getPlatformId() + "_" + + sendRtpItem.getChannelId() + "_" + + sendRtpItem.getStreamId() + "_" + + sendRtpItem.getCallId(); + redisTemplate.opsForValue().set(key, sendRtpItem); + } + + @Override + public SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId) { + if (platformGbId == null) { + platformGbId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (streamId == null) { + streamId = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + streamId + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + if (scan.size() > 0) { + return (SendRtpItem)redisTemplate.opsForValue().get(scan.get(0)); + }else { + return null; + } + } + + @Override + public List querySendRTPServerByChnnelId(String channelId) { + if (channelId == null) { + return null; + } + String platformGbId = "*"; + String callId = "*"; + String streamId = "*"; + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + streamId + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + List result = new ArrayList<>(); + for (Object o : scan) { + result.add((SendRtpItem) redisTemplate.opsForValue().get(o)); + } + return result; + } + + @Override + public List querySendRTPServerByStream(String stream) { + if (stream == null) { + return null; + } + String platformGbId = "*"; + String callId = "*"; + String channelId = "*"; + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + stream + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + List result = new ArrayList<>(); + for (Object o : scan) { + result.add((SendRtpItem) redisTemplate.opsForValue().get(o)); + } + return result; + } + + @Override + public List querySendRTPServer(String platformGbId) { + if (platformGbId == null) { + platformGbId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_*" + "_*" + "_*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpItem) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + /** + * 删除RTP推送信息缓存 + */ + @Override + public void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId) { + if (streamId == null) { + streamId = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + streamId + "_" + + callId; + List scan = RedisUtil.scan(redisTemplate, key); + if (scan.size() > 0) { + for (Object keyStr : scan) { + redisTemplate.delete(keyStr); + } + } + } + + @Override + public List queryAllSendRTPServer() { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*"; + List queryResult = RedisUtil.scan(redisTemplate, key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpItem) redisTemplate.opsForValue().get(keyItem)); + } + + return result; + } + + /** + * 查询某个通道是否存在上级点播(RTP推送) + */ + @Override + public boolean isChannelSendingRTP(String channelId) { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_*_" + + channelId + "*_" + "*_"; + List RtpStreams = RedisUtil.scan(redisTemplate, key); + return RtpStreams.size() > 0; + } + + @Override + public void clearCatchByDeviceId(String deviceId) { + List playLeys = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), + deviceId)); + if (playLeys.size() > 0) { + for (Object key : playLeys) { + redisTemplate.delete(key.toString()); + } + } + + List playBackers = RedisUtil.scan(redisTemplate, String.format("%S_%s_*_%s_*_*_*", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId)); + if (playBackers.size() > 0) { + for (Object key : playBackers) { + redisTemplate.delete(key.toString()); + } + } + + List deviceCache = RedisUtil.scan(redisTemplate, String.format("%S%s_%s", VideoManagerConstants.DEVICE_PREFIX, + userSetting.getServerId(), + deviceId)); + if (deviceCache.size() > 0) { + for (Object key : deviceCache) { + redisTemplate.delete(key.toString()); + } + } + } + + @Override + public void updateWVPInfo(JSONObject jsonObject, int time) { + String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetting.getServerId(); + redisTemplate.opsForValue().set(key, jsonObject, time); + } + + @Override + public void sendStreamChangeMsg(String type, JSONObject jsonObject) { + String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; + logger.info("[redis 流变化事件] {}: {}", key, jsonObject.toString()); + redisTemplate.convertAndSend(key, jsonObject); + } + + @Override + public void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, OnStreamChangedHookParam onStreamChangedHookParam) { + // 查找是否使用了callID + StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); + if (streamAuthorityInfo != null) { + onStreamChangedHookParam.setCallId(streamAuthorityInfo.getCallId()); + } + redisTemplate.opsForValue().set(key, onStreamChangedHookParam); + } + + @Override + public void removeStream(String mediaServerId, String type, String app, String streamId) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerId; + redisTemplate.delete(key); + } + + @Override + public StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId) { + if (stream == null && callId == null) { + return null; + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List streamInfoScan = RedisUtil.scan(redisTemplate, key); + if (streamInfoScan.size() > 0) { + return (StreamInfo) redisTemplate.opsForValue().get(streamInfoScan.get(0)); + }else { + return null; + } + } + + @Override + public ThirdPartyGB queryMemberNoGBId(String queryKey) { + String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey; + return JsonUtil.redisJsonToObject(redisTemplate, key, ThirdPartyGB.class); + } + + @Override + public void removeStream(String mediaServerId, String type) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + List streams = RedisUtil.scan(redisTemplate, key); + for (Object stream : streams) { + redisTemplate.delete(stream); + } + } + + @Override + public List getStreams(String mediaServerId, String type) { + List result = new ArrayList<>(); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + List streams = RedisUtil.scan(redisTemplate, key); + for (Object stream : streams) { + OnStreamChangedHookParam onStreamChangedHookParam = (OnStreamChangedHookParam)redisTemplate.opsForValue().get(stream); + result.add(onStreamChangedHookParam); + } + return result; + } + + @Override + public void updateDevice(Device device) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + device.getDeviceId(); + redisTemplate.opsForValue().set(key, device); + } + + @Override + public void removeDevice(String deviceId) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; + redisTemplate.delete(key); + } + + @Override + public void removeAllDevice() { + String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object key : keys) { + redisTemplate.delete(key); + } + } + + @Override + public List getAllDevices() { + String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + Device device = JsonUtil.redisJsonToObject(redisTemplate, key, Device.class); + if (Objects.nonNull(device)) { // 只取没有存过得 + result.add(JsonUtil.redisJsonToObject(redisTemplate, key, Device.class)); + } + } + + return result; + } + + @Override + public Device getDevice(String deviceId) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; + return JsonUtil.redisJsonToObject(redisTemplate, key, Device.class); + } + + @Override + public void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gpsMsgInfo.getId(); + redisTemplate.opsForValue().set(key, gpsMsgInfo, 60); // 默认GPS消息保存1分钟 + } + + @Override + public GPSMsgInfo getGpsMsgInfo(String gbId) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId; + return JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); + } + + @Override + public List getAllGpsMsgInfo() { + String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + GPSMsgInfo gpsMsgInfo = JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); + if (Objects.nonNull(gpsMsgInfo) && !gpsMsgInfo.isStored()) { // 只取没有存过得 + result.add(JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class)); + } + } + + return result; + } + + @Override + public void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream; + redisTemplate.opsForValue().set(key, streamAuthorityInfo); + } + + @Override + public void removeStreamAuthorityInfo(String app, String stream) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; + redisTemplate.delete(key); + } + + + + @Override + public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; + return JsonUtil.redisJsonToObject(redisTemplate, key, StreamAuthorityInfo.class); + + } + + @Override + public List getAllStreamAuthorityInfo() { + String scanKey = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_*_*" ; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(redisTemplate, scanKey); + for (Object o : keys) { + String key = (String) o; + result.add(JsonUtil.redisJsonToObject(redisTemplate, key, StreamAuthorityInfo.class)); + } + return result; + } + + + @Override + public OnStreamChangedHookParam getStreamInfo(String app, String streamId, String mediaServerId) { + String scanKey = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_*_" + app + "_" + streamId + "_" + mediaServerId; + + OnStreamChangedHookParam result = null; + List keys = RedisUtil.scan(redisTemplate, scanKey); + if (keys.size() > 0) { + String key = (String) keys.get(0); + result = JsonUtil.redisJsonToObject(redisTemplate, key, OnStreamChangedHookParam.class); + } + + return result; + } + + @Override + public void addCpuInfo(double cpuInfo) { + String key = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId(); + Map infoMap = new HashMap<>(); + infoMap.put("time", DateUtil.getNow()); + infoMap.put("data", String.valueOf(cpuInfo)); + redisTemplate.opsForList().rightPush(key, infoMap); + // 每秒一个,最多只存30个 + Long size = redisTemplate.opsForList().size(key); + if (size != null && size >= 30) { + for (int i = 0; i < size - 30; i++) { + redisTemplate.opsForList().leftPop(key); + } + } + } + + @Override + public void addMemInfo(double memInfo) { + String key = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId(); + Map infoMap = new HashMap<>(); + infoMap.put("time", DateUtil.getNow()); + infoMap.put("data", String.valueOf(memInfo)); + redisTemplate.opsForList().rightPush(key, infoMap); + // 每秒一个,最多只存30个 + Long size = redisTemplate.opsForList().size(key); + if (size != null && size >= 30) { + for (int i = 0; i < size - 30; i++) { + redisTemplate.opsForList().leftPop(key); + } + } + } + + @Override + public void addNetInfo(Map networkInterfaces) { + String key = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId(); + Map infoMap = new HashMap<>(); + infoMap.put("time", DateUtil.getNow()); + for (String netKey : networkInterfaces.keySet()) { + infoMap.put(netKey, networkInterfaces.get(netKey)); + } + redisTemplate.opsForList().rightPush(key, infoMap); + // 每秒一个,最多只存30个 + Long size = redisTemplate.opsForList().size(key); + if (size != null && size >= 30) { + for (int i = 0; i < size - 30; i++) { + redisTemplate.opsForList().leftPop(key); + } + } + } + + @Override + public void addDiskInfo(List> diskInfo) { + + String key = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId(); + redisTemplate.opsForValue().set(key, diskInfo); + } + + @Override + public SystemAllInfo getSystemInfo() { + String cpuKey = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId(); + String memKey = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId(); + String netKey = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId(); + String diskKey = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId(); + SystemAllInfo systemAllInfo = new SystemAllInfo(); + systemAllInfo.setCpu(redisTemplate.opsForList().range(cpuKey, 0, -1)); + systemAllInfo.setMem(redisTemplate.opsForList().range(memKey, 0, -1)); + systemAllInfo.setNet(redisTemplate.opsForList().range(netKey, 0, -1)); + + systemAllInfo.setDisk(redisTemplate.opsForValue().get(diskKey)); + systemAllInfo.setNetTotal(SystemInfoUtils.getNetworkTotal()); + return systemAllInfo; + } + + @Override + public void sendMobilePositionMsg(JSONObject jsonObject) { + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_MOBILE_POSITION; + logger.info("[redis发送通知] 移动位置 {}: {}", key, jsonObject.toString()); + redisTemplate.convertAndSend(key, jsonObject); + } + + @Override + public void sendStreamPushRequestedMsg(MessageForPushChannel msg) { + String key = VideoManagerConstants.VM_MSG_STREAM_PUSH_REQUESTED; + logger.info("[redis发送通知] 推流被请求 {}: {}/{}", key, msg.getApp(), msg.getStream()); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + } + + @Override + public void sendAlarmMsg(AlarmChannelMessage msg) { + // 此消息用于对接第三方服务下级来的消息内容 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM; + logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg)); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + } + + @Override + public boolean deviceIsOnline(String deviceId) { + Device device = getDevice(deviceId); + if (device == null) { + return false; + } + return device.getOnline() == 1; + // getDevice(deviceId).getOnline() == 1 + + } + + + @Override + public void sendStreamPushRequestedMsgForStatus() { + String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED; + logger.info("[redis通知]获取所有推流设备的状态"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(key, key); + redisTemplate.convertAndSend(key, jsonObject); + } + + @Override + public int getPushStreamCount(String id) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PUSH_*_*_" + id; + return RedisUtil.scan(redisTemplate, key).size(); + } + + @Override + public int getProxyStreamCount(String id) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PULL_*_*_" + id; + return RedisUtil.scan(redisTemplate, key).size(); + } + + @Override + public int getGbReceiveCount(String id) { + String playKey = VideoManagerConstants.PLAYER_PREFIX + "_" + userSetting.getServerId() + "_" + id + "_*"; + String playBackKey = VideoManagerConstants.PLAY_BLACK_PREFIX + "_" + userSetting.getServerId() + "_" + id + "_*"; + String downloadKey = VideoManagerConstants.DOWNLOAD_PREFIX + "_" + userSetting.getServerId() + "_" + id + "_*"; + + return RedisUtil.scan(redisTemplate, playKey).size() + RedisUtil.scan(redisTemplate, playBackKey).size() + RedisUtil.scan(redisTemplate, downloadKey).size(); + } + + @Override + public int getGbSendCount(String id) { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + id + "_*"; + return RedisUtil.scan(redisTemplate, key).size(); + } + + @Override + public void sendDeviceOrChannelStatus(String deviceId, String channelId, boolean online) { + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_DEVICE_STATUS; + logger.info("[redis通知] 推送设备/通道状态, {}/{}-{}", deviceId, channelId, online); + StringBuilder msg = new StringBuilder(); + msg.append(deviceId); + if (channelId != null) { + msg.append(":").append(channelId); + } + msg.append(" ").append(online? "ON":"OFF"); + + redisTemplate.convertAndSend(key, msg.toString()); + } + + @Override + public void sendPlatformStartPlayMsg(MessageForPushChannel msg) { + String key = VideoManagerConstants.VM_MSG_STREAM_START_PLAY_NOTIFY; + logger.info("[redis发送通知] 发送 推流被上级平台观看 {}: {}/{}->{}", key, msg.getApp(), msg.getStream(), msg.getPlatFormId()); + redisTemplate.convertAndSend(key, JSON.toJSON(msg)); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/storager/impl/VideoManagerStorageImpl.java b/riis-monitor/src/main/java/com/yfd/monitor/storager/impl/VideoManagerStorageImpl.java new file mode 100644 index 0000000..81ace55 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/storager/impl/VideoManagerStorageImpl.java @@ -0,0 +1,1042 @@ +package com.yfd.monitor.storager.impl; + +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.gdw2019.bean.*; +import com.yfd.monitor.gdw2019.event.EventPublisher; +import com.yfd.monitor.gdw2019.event.subscribe.catalog.CatalogEvent; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.service.bean.GPSMsgInfo; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.storager.dao.*; +import com.yfd.monitor.storager.dao.dto.ChannelSourceInfo; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import com.yfd.monitor.web.gdw2019.dto.DeviceChannelExtend; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Component; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 视频设备数据存储-jdbc实现 + * swwheihei + * 2020年5月6日 下午2:31:42 + */ +@SuppressWarnings("rawtypes") +@Component +public class VideoManagerStorageImpl implements IVideoManagerStorage { + + private final Logger logger = LoggerFactory.getLogger(VideoManagerStorageImpl.class); + + @Autowired + EventPublisher eventPublisher; + + @Autowired + SipConfig sipConfig; + + + @Resource + TransactionDefinition transactionDefinition; + + @Resource + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + private DeviceAlarmMapper deviceAlarmMapper; + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private StreamProxyMapper streamProxyMapper; + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private UserSetting userSetting; + + @Autowired + private PlatformCatalogMapper catalogMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private ParentPlatformMapper parentPlatformMapper; + + /** + * 根据设备ID判断设备是否存在 + * + * @param deviceId 设备ID + * @return true:存在 false:不存在 + */ + @Override + public boolean exists(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId) != null; + } + + @Override + public boolean resetChannels(String deviceId, List deviceChannelList) { + if (CollectionUtils.isEmpty(deviceChannelList)) { + return false; + } + List allChannels = deviceChannelMapper.queryAllChannels(deviceId); + Map allChannelMap = new ConcurrentHashMap<>(); + if (allChannels.size() > 0) { + for (DeviceChannel deviceChannel : allChannels) { + allChannelMap.put(deviceChannel.getChannelId(), deviceChannel); + } + } + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + // 数据去重 + List channels = new ArrayList<>(); + StringBuilder stringBuilder = new StringBuilder(); + Map subContMap = new HashMap<>(); + if (deviceChannelList.size() > 0) { + // 数据去重 + Set gbIdSet = new HashSet<>(); + for (DeviceChannel deviceChannel : deviceChannelList) { + if (!gbIdSet.contains(deviceChannel.getChannelId())) { + gbIdSet.add(deviceChannel.getChannelId()); + if (allChannelMap.containsKey(deviceChannel.getChannelId())) { + deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); + deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); + if (allChannelMap.get(deviceChannel.getChannelId()).getStatus() !=deviceChannel.getStatus()){ + List strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId()); + if (!CollectionUtils.isEmpty(strings)){ + strings.forEach(platformId->{ + eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.getStatus()==1?CatalogEvent.ON:CatalogEvent.OFF); + }); + } + + } + } + channels.add(deviceChannel); + if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { + if (subContMap.get(deviceChannel.getParentId()) == null) { + subContMap.put(deviceChannel.getParentId(), 1); + }else { + Integer count = subContMap.get(deviceChannel.getParentId()); + subContMap.put(deviceChannel.getParentId(), count++); + } + } + }else { + stringBuilder.append(deviceChannel.getChannelId()).append(","); + } + } + if (channels.size() > 0) { + for (DeviceChannel channel : channels) { + if (subContMap.get(channel.getChannelId()) != null){ + channel.setSubCount(subContMap.get(channel.getChannelId())); + } + } + } + + } + if (stringBuilder.length() > 0) { + logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); + } + if(CollectionUtils.isEmpty(channels)){ + logger.info("通道重设,数据为空={}" , deviceChannelList); + return false; + } + try { + int cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); + int limitCount = 300; + boolean result = cleanChannelsResult < 0; + if (!result && channels.size() > 0) { + if (channels.size() > limitCount) { + for (int i = 0; i < channels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > channels.size()) { + toIndex = channels.size(); + } + result = result || deviceChannelMapper.batchAdd(channels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchAdd(channels) < 0; + } + } + if (result) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + } + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + return true; + }catch (Exception e) { + logger.error("未处理的异常 ", e); + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + + } + + + @Override + public boolean updateChannels(String deviceId, List deviceChannelList) { + if (CollectionUtils.isEmpty(deviceChannelList)) { + return false; + } + List allChannels = deviceChannelMapper.queryAllChannels(deviceId); + Map allChannelMap = new ConcurrentHashMap<>(); + if (allChannels.size() > 0) { + for (DeviceChannel deviceChannel : allChannels) { + allChannelMap.put(deviceChannel.getChannelId(), deviceChannel); + } + } + List addChannels = new ArrayList<>(); + List updateChannels = new ArrayList<>(); + + + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + // 数据去重 + StringBuilder stringBuilder = new StringBuilder(); + Map subContMap = new HashMap<>(); + if (deviceChannelList.size() > 0) { + // 数据去重 + Set gbIdSet = new HashSet<>(); + for (DeviceChannel deviceChannel : deviceChannelList) { + if (!gbIdSet.contains(deviceChannel.getChannelId())) { + gbIdSet.add(deviceChannel.getChannelId()); + if (allChannelMap.containsKey(deviceChannel.getChannelId())) { + deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); + deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + updateChannels.add(deviceChannel); + }else { + deviceChannel.setCreateTime(DateUtil.getNow()); + addChannels.add(deviceChannel); + } + if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) { + if (subContMap.get(deviceChannel.getParentId()) == null) { + subContMap.put(deviceChannel.getParentId(), 1); + }else { + Integer count = subContMap.get(deviceChannel.getParentId()); + subContMap.put(deviceChannel.getParentId(), count++); + } + } + }else { + stringBuilder.append(deviceChannel.getChannelId()).append(","); + } + } + if (addChannels.size() > 0) { + for (DeviceChannel channel : addChannels) { + if (subContMap.get(channel.getChannelId()) != null){ + channel.setSubCount(subContMap.get(channel.getChannelId())); + } + } + } + if (updateChannels.size() > 0) { + for (DeviceChannel channel : updateChannels) { + if (subContMap.get(channel.getChannelId()) != null){ + channel.setSubCount(subContMap.get(channel.getChannelId())); + } + } + } + + } + if (stringBuilder.length() > 0) { + logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); + } + if(CollectionUtils.isEmpty(updateChannels) && CollectionUtils.isEmpty(addChannels) ){ + logger.info("通道更新,数据为空={}" , deviceChannelList); + return false; + } + try { + int limitCount = 300; + boolean result = false; + if (addChannels.size() > 0) { + if (addChannels.size() > limitCount) { + for (int i = 0; i < addChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > addChannels.size()) { + toIndex = addChannels.size(); + } + result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchAdd(addChannels) < 0; + } + } + if (updateChannels.size() > 0) { + if (updateChannels.size() > limitCount) { + for (int i = 0; i < updateChannels.size(); i += limitCount) { + int toIndex = i + limitCount; + if (i + limitCount > updateChannels.size()) { + toIndex = updateChannels.size(); + } + result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0; + } + } + if (result) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + }else { + //手动提交 + dataSourceTransactionManager.commit(transactionStatus); + } + return true; + }catch (Exception e) { + logger.error("未处理的异常 ", e); + dataSourceTransactionManager.rollback(transactionStatus); + return false; + } + } + + @Override + public void deviceChannelOnline(String deviceId, String channelId) { + deviceChannelMapper.online(deviceId, channelId); + } + + @Override + public void deviceChannelOffline(String deviceId, String channelId) { + deviceChannelMapper.offline(deviceId, channelId); + } + + @Override + public void startPlay(String deviceId, String channelId, String streamId) { + deviceChannelMapper.startPlay(deviceId, channelId, streamId); + } + + @Override + public void stopPlay(String deviceId, String channelId) { + deviceChannelMapper.stopPlay(deviceId, channelId); + } + + /** + * 获取设备 + * + * @param deviceId 设备ID + * @return Device 设备对象 + */ + @Override + public Device queryVideoDevice(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId); + } + + /** + * 根据摄像头ID,查询NVR设备 + * + * @param deviceId 摄像头ID + * @return Device NVR设备对象 + */ + @Override + public Device queryNvrDeviceByDeviceId(String deviceId, String channelId) { + // Device monitordevice = deviceMapper.getDeviceByChannel(deviceId, channelId); + Device monitordevice = deviceMapper.getDeviceByDeviceId(deviceId); + List NvrDeviceIdList = deviceChannelMapper.queryChannelByDeviceIp(monitordevice.getIp()); + if (NvrDeviceIdList.size() > 0) { + DeviceChannel NvrDeviceId = NvrDeviceIdList.get(0); + Device device = deviceMapper.getDeviceByDeviceId(NvrDeviceId.getDeviceId()); + // 暂时用customName字段传递channelId + device.setCustomName(NvrDeviceId.getChannelId()); + return device; + } + return null; + + } + + @Override + public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count) { + // 获取到所有正在播放的流 + PageHelper.startPage(page, count); + List all; + if (catalogUnderDevice != null && catalogUnderDevice) { + all = deviceChannelMapper.queryChannels(deviceId, deviceId, query, hasSubChannel, online,null); + // 海康设备的parentId是SIP id + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, sipConfig.getId(), query, hasSubChannel, online,null); + all.addAll(deviceChannels); + }else { + all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online,null); + } + return new PageInfo<>(all); + } + + @Override + public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit) { + return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, channelIds, null, query, hasSubChannel, online, start, limit); + } + + + @Override + public List queryChannelsByDeviceId(String deviceId,Boolean online,List channelIds) { + return deviceChannelMapper.queryChannels(deviceId, null,null, null, online,channelIds); + } + + @Override + public List queryChannelsByDeviceId(String deviceId, List channelIds, Boolean online) { + return deviceChannelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds); + } + + @Override + public PageInfo querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) { + PageHelper.startPage(page, count); + List all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, query, hasSubChannel, online,null); + return new PageInfo<>(all); + } + + @Override + public DeviceChannel queryChannel(String deviceId, String channelId) { + return deviceChannelMapper.queryChannel(deviceId, channelId); + } + + + @Override + public int delChannel(String deviceId, String channelId) { + return deviceChannelMapper.del(deviceId, channelId); + } + + /** + * 获取多个设备 + * + * @param page 当前页数 + * @param count 每页数量 + * @return PageInfo 分页设备对象数组 + */ + @Override + public PageInfo queryVideoDeviceList(int page, int count, Boolean online) { + PageHelper.startPage(page, count); + List all = deviceMapper.getDevices(online); + return new PageInfo<>(all); + } + + /** + * 获取多个设备 + * + * @return List 设备对象数组 + */ + @Override + public List queryVideoDeviceList(Boolean online) { + + List deviceList = deviceMapper.getDevices(online); + return deviceList; + } + + /** + * 清空通道 + * @param deviceId + */ + @Override + public void cleanChannelsForDevice(String deviceId) { + deviceChannelMapper.cleanChannelsByDeviceId(deviceId); + } + + + + @Override + public boolean addParentPlatform(ParentPlatform parentPlatform) { + if (parentPlatform.getCatalogId() == null) { + parentPlatform.setCatalogId(parentPlatform.getServerGBId()); + } + int result = platformMapper.addParentPlatform(parentPlatform); + return result > 0; + } + + @Override + public boolean updateParentPlatform(ParentPlatform parentPlatform) { + int result = 0; + if (parentPlatform.getCatalogGroup() == 0) { + parentPlatform.setCatalogGroup(1); + } + if (parentPlatform.getAdministrativeDivision() == null) { + parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision()); + } + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); // .getDeviceGBId()); + if (parentPlatform.getId() == null ) { + if (parentPlatform.getCatalogId() == null) { + parentPlatform.setCatalogId(parentPlatform.getServerGBId()); + } + result = platformMapper.addParentPlatform(parentPlatform); + if (parentPlatformCatch == null) { + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + } + }else { + if (parentPlatformCatch == null) { // serverGBId 已变化 + ParentPlatform parentPlatById = platformMapper.getParentPlatById(parentPlatform.getId()); + // 使用旧的查出缓存ID + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformCatchInfo(parentPlatById.getServerGBId()); + } + + result = platformMapper.updateParentPlatform(parentPlatform); + } + // 更新缓存 + parentPlatformCatch.setParentPlatform(parentPlatform); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + + return result > 0; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean deleteParentPlatform(ParentPlatform parentPlatform) { + int result = platformMapper.delParentPlatform(parentPlatform); + // 删除关联的通道 + platformChannelMapper.cleanChannelForGB(parentPlatform.getServerGBId()); + return result > 0; + } + + @Override + public ParentPlatform queryParentPlatByServerGBId(String platformGbId) { + return platformMapper.getParentPlatByServerGBId(platformGbId); + } + + @Override + public List queryEnableParentPlatformList(boolean enable) { + return platformMapper.getEnableParentPlatformList(enable); + } + + @Override + public List queryEnablePlatformListWithAsMessageChannel() { + return platformMapper.queryEnablePlatformListWithAsMessageChannel(); + } + + @Override + public List queryDeviceWithAsMessageChannel() { + return deviceMapper.queryDeviceWithAsMessageChannel(); + } + + @Override + public void outlineForAllParentPlatform() { + platformMapper.outlineForAllParentPlatform(); + } + + + @Override + public PageInfo queryAllChannelList(int page, int count, String query, Boolean online, + Boolean channelType, String platformId, String catalogId) { + PageHelper.startPage(page, count); + List all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, catalogId); + return new PageInfo<>(all); + } + + @Override + public List queryChannelListInParentPlatform(String platformId) { + + return deviceChannelMapper.queryChannelByPlatformId(platformId); + } + + + @Override + public int delChannelForGB(String platformId, List channelReduces) { + + int result = platformChannelMapper.delChannelForGB(platformId, channelReduces); + List deviceChannelList = new ArrayList<>(); + for (ChannelReduce channelReduce : channelReduces) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(channelReduce.getChannelId()); + deviceChannelList.add(deviceChannel); + } + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + return result; + } + + @Override + public DeviceChannel queryChannelInParentPlatform(String platformId, String channelId) { + List channels = platformChannelMapper.queryChannelInParentPlatform(platformId, channelId); + if (channels.size() > 1) { + // 出现长度大于0的时候肯定是国标通道的ID重复了 + logger.warn("国标ID存在重复:{}", channelId); + } + if (channels.size() == 0) { + return null; + }else { + return channels.get(0); + } + } + + @Override + public List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId) { + List catalogs = platformChannelMapper.queryChannelInParentPlatformAndCatalog(platformId, catalogId); + return catalogs; + } + + @Override + public List queryStreamInParentPlatformAndCatalog(String platformId, String catalogId) { + List catalogs = platformGbStreamMapper.queryChannelInParentPlatformAndCatalogForCatalog(platformId, catalogId); + return catalogs; + } + + @Override + public Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId) { + List devices = platformChannelMapper.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId); + if (devices.size() > 1) { + // 出现长度大于0的时候肯定是国标通道的ID重复了 + logger.warn("国标ID存在重复:{}", channelId); + } + if (devices.size() == 0) { + return null; + }else { + return devices.get(0); + } + + + } + + @Override + public Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId) { + List devices = platformChannelMapper.queryDeviceInfoByPlatformIdAndChannelId(platformId, channelId); + if (devices.size() > 1) { + // 出现长度大于0的时候肯定是国标通道的ID重复了 + logger.warn("国标ID存在重复:{}", channelId); + } + if (devices.size() == 0) { + return null; + }else { + return devices.get(0); + } + } + + + + /** + * 移除代理流 + * @param app + * @param stream + * @return + */ + @Override + public int deleteStreamProxy(String app, String stream) { + return streamProxyMapper.del(app, stream); + } + + /** + * 根据是否启用获取代理流列表 + * @param enable + * @return + */ + @Override + public List getStreamProxyListForEnable(boolean enable) { + return streamProxyMapper.selectForEnable(enable); + } + + /** + * 分页查询代理流列表 + * @param page + * @param count + * @return + */ + @Override + public PageInfo queryStreamProxyList(Integer page, Integer count) { + PageHelper.startPage(page, count); + List all = streamProxyMapper.selectAll(); + return new PageInfo<>(all); + } + + /** + * 根据国标ID获取平台关联的直播流 + * @param platformId + * @param gbId + * @return + */ + @Override + public GbStream queryStreamInParentPlatform(String platformId, String gbId) { + return gbStreamMapper.queryStreamInPlatform(platformId, gbId); + } + + /** + * 获取平台关联的直播流 + * @param platformId + * @return + */ + @Override + public List queryGbStreamListInPlatform(String platformId) { + return gbStreamMapper.queryGbStreamListInPlatform(platformId, userSetting.isUsePushingAsStatus()); + } + + /** + * 按照是app和stream获取代理流 + * @param app + * @param stream + * @return + */ + @Override + public StreamProxyItem queryStreamProxy(String app, String stream){ + return streamProxyMapper.selectOne(app, stream); + } + + @Override + public int removeMedia(String app, String stream) { + return streamPushMapper.del(app, stream); + } + + @Override + public int mediaOffline(String app, String stream) { + GbStream gbStream = gbStreamMapper.selectOne(app, stream); + int result; + if ("proxy".equals(gbStream.getStreamType())) { + result = streamProxyMapper.updateStatus(app, stream, false); + }else { + result = streamPushMapper.updatePushStatus(app, stream, false); + } + return result; + } + + @Override + public int mediaOnline(String app, String stream) { + GbStream gbStream = gbStreamMapper.selectOne(app, stream); + int result; + if ("proxy".equals(gbStream.getStreamType())) { + result = streamProxyMapper.updateStatus(app, stream, true); + }else { + result = streamPushMapper.updatePushStatus(app, stream, true); + } + return result; + } + + @Override + public void updateParentPlatformStatus(String platformGbID, boolean online) { + platformMapper.updateParentPlatformStatus(platformGbID, online); + } + + @Override + public List getStreamProxyListForEnableInMediaServer(String id, boolean enable) { + return streamProxyMapper.selectForEnableInMediaServer(id, enable); + } + + + @Override + public Device queryVideoDeviceByChannelId( String channelId) { + Device result = null; + List channelList = deviceChannelMapper.queryChannelByChannelId(channelId); + if (channelList.size() == 1) { + result = deviceMapper.getDeviceByDeviceId(channelList.get(0).getDeviceId()); + } + return result; + } + + @Override + public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) { + return streamProxyMapper.selectOne(app, streamId); + } + + @Override + public List getChildrenCatalogByPlatform(String platformId, String parentId) { + return catalogMapper.selectByParentId(platformId, parentId); + } + + @Override + public int addCatalog(PlatformCatalog platformCatalog) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(platformCatalog.getPlatformId()); + if (platform == null) { + return 0; + } + if (platformCatalog.getId().length() <= 8) { + platformCatalog.setCivilCode(platformCatalog.getParentId()); + }else { + if (platformCatalog.getId().length() != 20) { + return 0; + } + if (platformCatalog.getParentId() != null) { + switch (Integer.parseInt(platformCatalog.getId().substring(10, 13))){ + case 200: + case 215: + if (platformCatalog.getParentId().length() <= 8) { + platformCatalog.setCivilCode(platformCatalog.getParentId()); + }else { + PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platformCatalog.getPlatformId(), platformCatalog.getParentId()); + if (catalog != null) { + platformCatalog.setCivilCode(catalog.getCivilCode()); + } + } + break; + case 216: + if (platformCatalog.getParentId().length() <= 8) { + platformCatalog.setCivilCode(platformCatalog.getParentId()); + }else { + PlatformCatalog catalog = catalogMapper.selectByPlatFormAndCatalogId(platformCatalog.getPlatformId(),platformCatalog.getParentId()); + if (catalog == null) { + logger.warn("[添加目录] 无法获取目录{}的CivilCode和BusinessGroupId", platformCatalog.getPlatformId()); + break; + } + platformCatalog.setCivilCode(catalog.getCivilCode()); + if (Integer.parseInt(platformCatalog.getParentId().substring(10, 13)) == 215) { + platformCatalog.setBusinessGroupId(platformCatalog.getParentId()); + }else { + if (Integer.parseInt(platformCatalog.getParentId().substring(10, 13)) == 216) { + platformCatalog.setBusinessGroupId(catalog.getBusinessGroupId()); + } + } + } + break; + default: + break; + } + } + } + int result = catalogMapper.add(platformCatalog); + if (result > 0) { + DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog); + eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.ADD); + } + return result; + } + + private PlatformCatalog getTopCatalog(String id, String platformId) { + PlatformCatalog catalog = catalogMapper.selectParentCatalog(id); + if (catalog.getParentId().equals(platformId)) { + return catalog; + }else { + return getTopCatalog(catalog.getParentId(), platformId); + } + } + + @Override + public PlatformCatalog getCatalog(String id) { + return catalogMapper.select(id); + } + + @Override + public int delCatalog(String id) { + PlatformCatalog platformCatalog = catalogMapper.select(id); + if (platformCatalog.getChildrenCount() > 0) { + List platformCatalogList = catalogMapper.selectByParentId(platformCatalog.getPlatformId(), platformCatalog.getId()); + for (PlatformCatalog catalog : platformCatalogList) { + if (catalog.getChildrenCount() == 0) { + delCatalogExecute(catalog.getId(), catalog.getPlatformId()); + }else { + delCatalog(catalog.getId()); + } + } + } + return delCatalogExecute(id, platformCatalog.getPlatformId()); + } + private int delCatalogExecute(String id, String platformId) { + int delresult = catalogMapper.del(id); + DeviceChannel deviceChannelForCatalog = new DeviceChannel(); + if (delresult > 0){ + deviceChannelForCatalog.setChannelId(id); + eventPublisher.catalogEventPublish(platformId, deviceChannelForCatalog, CatalogEvent.DEL); + } + + List gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformId, id); + if (gbStreams.size() > 0){ + List deviceChannelList = new ArrayList<>(); + for (GbStream gbStream : gbStreams) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + deviceChannelList.add(deviceChannel); + } + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + } + int delStreamresult = platformGbStreamMapper.delByCatalogId(id); + List platformCatalogs = platformChannelMapper.queryChannelInParentPlatformAndCatalog(platformId, id); + if (platformCatalogs.size() > 0){ + List deviceChannelList = new ArrayList<>(); + for (PlatformCatalog platformCatalog : platformCatalogs) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(platformCatalog.getId()); + deviceChannelList.add(deviceChannel); + } + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.DEL); + } + int delChannelresult = platformChannelMapper.delByCatalogId(id); + return delresult + delChannelresult + delStreamresult; + } + + + @Override + public int updateCatalog(PlatformCatalog platformCatalog) { + int result = catalogMapper.update(platformCatalog); + if (result > 0) { + DeviceChannel deviceChannel = getDeviceChannelByCatalog(platformCatalog); + eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.UPDATE); + } + return result; + } + + @Override + public int setDefaultCatalog(String platformId, String catalogId) { + return platformMapper.setDefaultCatalog(platformId, catalogId, DateUtil.getNow()); + } + + @Override + public List queryCatalogInPlatform(String platformId) { + return catalogMapper.queryCatalogInPlatform(platformId); + } + + @Override + public int delRelation(PlatformCatalog platformCatalog) { + if (platformCatalog.getType() == 1) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(platformCatalog.getId()); + eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.DEL); + return platformChannelMapper.delByCatalogIdAndChannelIdAndPlatformId(platformCatalog); + }else if (platformCatalog.getType() == 2) { + List gbStreams = platformGbStreamMapper.queryChannelInParentPlatformAndCatalog(platformCatalog.getPlatformId(), platformCatalog.getParentId()); + for (GbStream gbStream : gbStreams) { + if (gbStream.getGbId().equals(platformCatalog.getId())) { + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(gbStream.getGbId()); + eventPublisher.catalogEventPublish(platformCatalog.getPlatformId(), deviceChannel, CatalogEvent.DEL); + return platformGbStreamMapper.delByAppAndStream(gbStream.getApp(), gbStream.getStream()); + } + } + } + return 0; + } + + @Override + public int updateStreamGPS(List gpsMsgInfos) { + return gbStreamMapper.updateStreamGPS(gpsMsgInfos); + } + + + private DeviceChannel getDeviceChannelByCatalog(PlatformCatalog catalog) { + ParentPlatform platform = platformMapper.getParentPlatByServerGBId(catalog.getPlatformId()); + DeviceChannel deviceChannel = new DeviceChannel(); + deviceChannel.setChannelId(catalog.getId()); + deviceChannel.setName(catalog.getName()); + deviceChannel.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("JY"); + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + + deviceChannel.setRegisterWay(1); + deviceChannel.setParentId(catalog.getParentId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + + deviceChannel.setModel("live"); + deviceChannel.setOwner("MonitorServer"); + deviceChannel.setSecrecy("0"); + return deviceChannel; + } + + @Override + public List queryOnlineChannelsByDeviceId(String deviceId) { + return deviceChannelMapper.queryOnlineChannelsByDeviceId(deviceId); + } + + @Override + public List queryPlatFormListForGBWithGBId(String channelId, List platforms) { + return platformChannelMapper.queryPlatFormListForGBWithGBId(channelId, platforms); + } + + @Override + public List queryPlatFormListForStreamWithGBId(String app, String stream, List platforms) { + if (platforms == null || platforms.size() == 0) { + return new ArrayList<>(); + } + return platformGbStreamMapper.queryPlatFormListForGBWithGBId(app, stream, platforms); + } + + @Override + public GbStream getGbStream(String app, String streamId) { + return gbStreamMapper.selectOne(app, streamId); + } + + @Override + public void delCatalogByPlatformId(String serverGBId) { + catalogMapper.delByPlatformId(serverGBId); + } + + @Override + public void delRelationByPlatformId(String serverGBId) { + platformGbStreamMapper.delByPlatformId(serverGBId); + platformChannelMapper.delByPlatformId(serverGBId); + } + + @Override + public PlatformCatalog queryDefaultCatalogInPlatform(String platformId) { + return catalogMapper.selectDefaultByPlatFormId(platformId); + } + + @Override + public List getChannelSource(String platformId, String gbId) { + return platformMapper.getChannelSource(platformId, gbId); + } + + @Override + public void updateChannelPosition(DeviceChannel deviceChannel) { + if (deviceChannel.getChannelId().equals(deviceChannel.getDeviceId())) { + deviceChannel.setChannelId(null); + } + if (deviceChannel.getGpsTime() == null) { + deviceChannel.setGpsTime(DateUtil.getNow()); + } + + // deviceChannelMapper.updatePosition(deviceChannel); + } + + @Override + public void cleanContentForPlatform(String serverGBId) { + catalogMapper.delByPlatformId(serverGBId); + platformChannelMapper.delByPlatformId(serverGBId); + platformGbStreamMapper.delByPlatformId(serverGBId); + } + + @Override + public List queryChannelWithCatalog(String CatalogId) { + return deviceChannelMapper.queryChannelWithCatalog(CatalogId); + } + + @Override + public List queryChannelWithPlatform(String serverGBId) { + return deviceChannelMapper.queryChannelWithPlatform(serverGBId); + } + + @Override + public boolean updatePatroldeviceTime(String id, String prepos) { + deviceMapper.updatePatroldeviceTime( id, prepos); + return true; + } + + @Override + public List getRiisPatrolDeviceByCode(String deviceId) { + List list= deviceMapper.getRiisPatrolDeviceByCode(deviceId); + return list; + } + + @Override + public List getRiisPatrolDeviceByRobot(String robotCode) { + List list= deviceMapper.getRiisPatrolDeviceByRobot(robotCode); + return list; + } + + @Override + public List> queryHistoryAlarmlog(String code, String userCode, String type, String beginTime, + String endTime, String level, int fromIndex, int size) { + return deviceAlarmMapper.queryHistoryAlarmlog(code,userCode,type,beginTime,endTime,level,fromIndex,size); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/CloudRecordUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/CloudRecordUtils.java new file mode 100644 index 0000000..61231b7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/CloudRecordUtils.java @@ -0,0 +1,22 @@ +package com.yfd.monitor.utils; + +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.bean.DownloadFileInfo; + +public class CloudRecordUtils { + + public static DownloadFileInfo getDownloadFilePath(MediaServerItem mediaServerItem, String filePath) { + DownloadFileInfo downloadFileInfo = new DownloadFileInfo(); + + String pathTemplate = "%s://%s:%s/index/api/downloadFile?file_path=" + filePath; + + downloadFileInfo.setHttpPath(String.format(pathTemplate, "http", mediaServerItem.getStreamIp(), + mediaServerItem.getHttpPort())); + + if (mediaServerItem.getHttpSSlPort() > 0) { + downloadFileInfo.setHttpsPath(String.format(pathTemplate, "https", mediaServerItem.getStreamIp(), + mediaServerItem.getHttpSSlPort())); + } + return downloadFileInfo; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/Coordtransform.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/Coordtransform.java new file mode 100644 index 0000000..6e4c27b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/Coordtransform.java @@ -0,0 +1,125 @@ +package com.yfd.monitor.utils; + +/** + * 坐标转换 + * 一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具类 + * 参考https://github.com/wandergis/coordtransform 写的Java版本 + * @date 2016-03-18 + * @url https://github.com/xinconan/coordtransform + */ +public class Coordtransform { + + private static double x_PI = 3.14159265358979324 * 3000.0 / 180.0; + private static double PI = 3.1415926535897932384626; + private static double a = 6378245.0; + private static double ee = 0.00669342162296594323; + + /** + * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 + * 即 百度 转 谷歌、高德 + * @param bd_lon + * @param bd_lat + * @return Double[lon,lat] + */ + public static Double[] BD09ToGCJ02(Double bd_lon,Double bd_lat){ + double x = bd_lon - 0.0065; + double y = bd_lat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta); + arr[1] = z * Math.sin(theta); + return arr; + } + + /** + * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 + * 即谷歌、高德 转 百度 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToBD09(Double gcj_lon,Double gcj_lat){ + double z = Math.sqrt(gcj_lon * gcj_lon + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI); + double theta = Math.atan2(gcj_lat, gcj_lon) + 0.000003 * Math.cos(gcj_lon * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta) + 0.0065; + arr[1] = z * Math.sin(theta) + 0.006; + return arr; + } + + /** + * WGS84转GCJ02 + * @param wgs_lon + * @param wgs_lat + * @return Double[lon,lat] + */ + public static Double[] WGS84ToGCJ02(Double wgs_lon,Double wgs_lat){ + if(outOfChina(wgs_lon, wgs_lat)){ + return new Double[]{wgs_lon,wgs_lat}; + } + double dlat = transformlat(wgs_lon - 105.0, wgs_lat - 35.0); + double dlng = transformlng(wgs_lon - 105.0, wgs_lat - 35.0); + double radlat = wgs_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + Double[] arr = new Double[2]; + arr[0] = wgs_lon + dlng; + arr[1] = wgs_lat + dlat; + return arr; + } + + /** + * GCJ02转WGS84 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToWGS84(Double gcj_lon,Double gcj_lat){ + if(outOfChina(gcj_lon, gcj_lat)){ + return new Double[]{gcj_lon,gcj_lat}; + } + double dlat = transformlat(gcj_lon - 105.0, gcj_lat - 35.0); + double dlng = transformlng(gcj_lon - 105.0, gcj_lat - 35.0); + double radlat = gcj_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + double mglat = gcj_lat + dlat; + double mglng = gcj_lon + dlng; + return new Double[]{gcj_lon * 2 - mglng, gcj_lat * 2 - mglat}; + } + + private static Double transformlat(double lng, double lat) { + double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret; + } + + private static Double transformlng(double lng,double lat) { + double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret; + } + + /** + * outOfChina + * @描述: 判断是否在国内,不在国内则不做偏移 + * @param lng + * @param lat + * @return {boolean} + */ + private static boolean outOfChina(Double lng,Double lat) { + return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/DateUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/DateUtil.java new file mode 100644 index 0000000..1a46966 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/DateUtil.java @@ -0,0 +1,150 @@ +package com.yfd.monitor.utils; + + +import org.apache.commons.lang3.ObjectUtils; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; +import java.util.Locale; + +/** + * 全局时间工具类 + * @author lin + */ +public class DateUtil { + + /** + * 兼容不规范的iso8601时间格式 + */ + private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s"; + + /** + * 用以输出标准的iso8601时间格式 + */ + private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; + + /** + * wvp内部统一时间格式 + */ + public static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; + + /** + * wvp内部统一时间格式 + */ + public static final String URL_PATTERN = "yyyyMMddHHmmss"; + + /** + * 日期格式 + */ + public static final String date_PATTERN = "yyyy-MM-dd"; + + public static final String zoneStr = "Asia/Shanghai"; + + public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter DateFormatter = DateTimeFormatter.ofPattern(date_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter urlFormatter = DateTimeFormatter.ofPattern(URL_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + + public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { + + return formatterISO8601.format(formatter.parse(formatTime)); + } + + public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { + return formatter.format(formatterCompatibleISO8601.parse(formatTime)); + } + + public static String urlToyyyy_MM_dd_HH_mm_ss(String formatTime) { + return formatter.format(urlFormatter.parse(formatTime)); + } + + /** + * yyyy_MM_dd_HH_mm_ss 转时间戳 + * @param formatTime + * @return + */ + public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { + TemporalAccessor temporalAccessor = formatter.parse(formatTime); + Instant instant = Instant.from(temporalAccessor); + return instant.getEpochSecond(); + } + + /** + * 时间戳 转 yyyy_MM_dd_HH_mm_ss + */ + public static String timestampTo_yyyy_MM_dd_HH_mm_ss(long timestamp) { + Instant instant = Instant.ofEpochSecond(timestamp); + return formatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr))); + } + + /** + * 时间戳 转 yyyy_MM_dd + */ + public static String timestampTo_yyyy_MM_dd(long timestamp) { + Instant instant = Instant.ofEpochMilli(timestamp); + return DateFormatter.format(LocalDateTime.ofInstant(instant, ZoneId.of(zoneStr))); + } + + /** + * 获取当前时间 + * @return + */ + public static String getNow() { + LocalDateTime nowDateTime = LocalDateTime.now(); + return formatter.format(nowDateTime); + } + + /** + * 获取当前时间 + * @return + */ + public static String getNowForUrl() { + LocalDateTime nowDateTime = LocalDateTime.now(); + return urlFormatter.format(nowDateTime); + } + + + /** + * 格式校验 + * @param timeStr 时间字符串 + * @param dateTimeFormatter 待校验的格式 + * @return + */ + public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { + try { + LocalDate.parse(timeStr, dateTimeFormatter); + return true; + }catch (DateTimeParseException exception) { + return false; + } + } + + public static String getNowForISO8601() { + LocalDateTime nowDateTime = LocalDateTime.now(); + return formatterISO8601.format(nowDateTime); + } + + public static long getDifferenceForNow(String keepaliveTime) { + if (ObjectUtils.isEmpty(keepaliveTime)) { + return 0; + } + Instant beforeInstant = Instant.from(formatter.parse(keepaliveTime)); + return ChronoUnit.MILLIS.between(beforeInstant, Instant.now()); + } + + public static long getDifference(String startTime, String endTime) { + if (ObjectUtils.isEmpty(startTime) || ObjectUtils.isEmpty(endTime)) { + return 0; + } + Instant startInstant = Instant.from(formatter.parse(startTime)); + Instant endInstant = Instant.from(formatter.parse(endTime)); + return ChronoUnit.MILLIS.between(endInstant, startInstant); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/FFmpegStreamPusher.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/FFmpegStreamPusher.java new file mode 100644 index 0000000..f762ac4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/FFmpegStreamPusher.java @@ -0,0 +1,12 @@ +package com.yfd.monitor.utils; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; + +public class FFmpegStreamPusher { + + public static void main(String[] args) { + + } +} \ No newline at end of file diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/GitUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/GitUtil.java new file mode 100644 index 0000000..64dd86d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/GitUtil.java @@ -0,0 +1,59 @@ +package com.yfd.monitor.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 一个优秀的颓废程序猿(CSDN) + */ +@Component +@PropertySource(value = {"classpath:git.properties" }, ignoreResourceNotFound = true) +public class GitUtil { + + @Value("${git.branch:}") + private String branch; + @Value("${git.commit.id:}") + private String gitCommitId; + @Value("${git.remote.origin.url:}") + private String gitUrl; + @Value("${git.build.time:}") + private String buildDate; + + @Value("${git.build.version:}") + private String buildVersion; + + @Value("${git.commit.id.abbrev:}") + private String commitIdShort; + + @Value("${git.commit.time:}") + private String commitTime; + + public String getGitCommitId() { + return gitCommitId; + } + + public String getBranch() { + return branch; + } + + public String getGitUrl() { + return gitUrl; + } + + public String getBuildDate() { + return buildDate; + } + + public String getCommitIdShort() { + return commitIdShort; + } + + public String getBuildVersion() { + return buildVersion; + } + + public String getCommitTime() { + return commitTime; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/GpsUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/GpsUtil.java new file mode 100644 index 0000000..aef0599 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/GpsUtil.java @@ -0,0 +1,37 @@ +package com.yfd.monitor.utils; + +import com.yfd.monitor.gdw2019.bean.BaiduPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Base64; + +public class GpsUtil { + + private static Logger logger = LoggerFactory.getLogger(GpsUtil.class); + + public static BaiduPoint Wgs84ToBd09(String xx, String yy) { + + + double lng = Double.parseDouble(xx); + double lat = Double.parseDouble(yy); + Double[] gcj02 = Coordtransform.WGS84ToGCJ02(lng, lat); + Double[] doubles = Coordtransform.GCJ02ToBD09(gcj02[0], gcj02[1]); + BaiduPoint bdPoint= new BaiduPoint(); + bdPoint.setBdLng(doubles[0] + ""); + bdPoint.setBdLat(doubles[1] + ""); + return bdPoint; + } + + /** + * BASE64解码 + * @param str + * @return string + */ + public static byte[] decode(String str) { + byte[] bt = null; + final Base64.Decoder decoder = Base64.getDecoder(); + bt = decoder.decode(str); // .decodeBuffer(str); + return bt; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/HttpUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/HttpUtils.java new file mode 100644 index 0000000..1f18a30 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/HttpUtils.java @@ -0,0 +1,88 @@ +package com.yfd.monitor.utils; + +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HttpUtils { + + public static void main(String[] args) { + String serverUrl = "http://192.168.24.56/upload_file/picture"; + String filePath = "path/to/your/file/195110000003010003_20180312T123846Z.jpg"; + + try { + uploadFile(serverUrl, filePath); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static boolean uploadFile(String serverUrl, String filePath) { + File file = new File(filePath); + String boundary = "-----------------------------7db372eb000e2"; + String lineEnd = "\r\n"; + String twoHyphens = "--"; + + try { + URL url = new URL(serverUrl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setUseCaches(false); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Accept", "text/plain, */*"); + conn.setRequestProperty("Accept-Language", "zh-cn"); + + // Extract host from serverUrl + String host = url.getHost(); + conn.setRequestProperty("Host", host); + + conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); + conn.setRequestProperty("User-Agent", "WinHttpClient"); + + DataOutputStream dos = new DataOutputStream(conn.getOutputStream()); + + dos.writeBytes(twoHyphens + boundary + lineEnd); + dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"" + lineEnd); + dos.writeBytes("Content-Type: image/jpeg" + lineEnd); + dos.writeBytes(lineEnd); + + FileInputStream fileInputStream = new FileInputStream(file); + int bytesAvailable = fileInputStream.available(); + int maxBufferSize = 1024; + int bufferSize = Math.min(bytesAvailable, maxBufferSize); + byte[] buffer = new byte[bufferSize]; + + int bytesRead = fileInputStream.read(buffer, 0, bufferSize); + + while (bytesRead > 0) { + dos.write(buffer, 0, bufferSize); + bytesAvailable = fileInputStream.available(); + bufferSize = Math.min(bytesAvailable, maxBufferSize); + bytesRead = fileInputStream.read(buffer, 0, bufferSize); + } + + dos.writeBytes(lineEnd); + dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd); + + fileInputStream.close(); + dos.flush(); + dos.close(); + + int responseCode = conn.getResponseCode(); + conn.disconnect(); + + return responseCode == HttpURLConnection.HTTP_OK; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/JsonUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/JsonUtil.java new file mode 100644 index 0000000..a1451a9 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/JsonUtil.java @@ -0,0 +1,57 @@ +package com.yfd.monitor.utils; + +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Objects; + +/** + * JsonUtil + * + * @version 1.0.0 + * @since 2023/2/2 15:24 + */ +public final class JsonUtil { + + private JsonUtil() { + } + + /** + * safe json type conversion + * + * @param key redis key + * @param clazz cast type + * @param + * @return result type + */ + public static T redisJsonToObject(RedisTemplate redisTemplate, String key, Class clazz) { + Object jsonObject = redisTemplate.opsForValue().get(key); + if (Objects.isNull(jsonObject)) { + return null; + } + return clazz.cast(jsonObject); + } + + public static void main(String[] args) { + int alarmType = 11111111; // 替换为你的实际32位告警类型值 + + // 截取每位字节 + int videoLoss = (alarmType & 0x01) >> 0; + int motionDetection = (alarmType & 0x02) >> 1; + int videoMask = (alarmType & 0x04) >> 2; + int highTemperature = (alarmType & 0x100) >> 8; + int lowTemperature = (alarmType & 0x200) >> 9; + int fanFailure = (alarmType & 0x400) >> 10; + int diskFailure = (alarmType & 0x800) >> 11; + int statusEvent = (alarmType & 0x10000) >> 16; + + // 打印告警类型 + System.out.println("Video Loss: " + videoLoss); + System.out.println("Motion Detection: " + motionDetection); + System.out.println("Video Mask: " + videoMask); + System.out.println("High Temperature: " + highTemperature); + System.out.println("Low Temperature: " + lowTemperature); + System.out.println("Fan Failure: " + fanFailure); + System.out.println("Disk Failure: " + diskFailure); + System.out.println("Status Event: " + statusEvent); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/SpringBeanFactory.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/SpringBeanFactory.java new file mode 100644 index 0000000..329aba0 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/SpringBeanFactory.java @@ -0,0 +1,50 @@ +package com.yfd.monitor.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * @description:spring bean获取工厂,获取spring中的已初始化的bean +* + * @date: 2019年6月25日 下午4:51:52 + * + */ +@Component +public class SpringBeanFactory implements ApplicationContextAware { + + // Spring应用上下文环境 + private static ApplicationContext applicationContext; + + /** + * 实现ApplicationContextAware接口的回调方法,设置上下文环境 + */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + SpringBeanFactory.applicationContext = applicationContext; + } + + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + /** + * 获取对象 这里重写了bean方法,起主要作用 + */ + public static T getBean(String beanId) throws BeansException { + if (applicationContext == null) { + return null; + } + return (T) applicationContext.getBean(beanId); + } + + /** + * 获取当前环境 + */ + public static String getActiveProfile() { + return applicationContext.getEnvironment().getActiveProfiles()[0]; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/SystemInfoUtils.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/SystemInfoUtils.java new file mode 100644 index 0000000..710671e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/SystemInfoUtils.java @@ -0,0 +1,144 @@ +package com.yfd.monitor.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import oshi.SystemInfo; +import oshi.hardware.*; +import oshi.software.os.OperatingSystem; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 实现参考自xiaozhangnomoney原创文章, + * 版权声明:本文为xiaozhangnomoney原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明 + * 原文出处链接:https://blog.csdn.net/xiaozhangnomoney/article/details/107769147 + */ +public class SystemInfoUtils { + + private final static Logger logger = LoggerFactory.getLogger(SystemInfoUtils.class); + + /** + * 获取cpu信息 + * @return + * @throws InterruptedException + */ + public static double getCpuInfo() throws InterruptedException { + SystemInfo systemInfo = new SystemInfo(); + CentralProcessor processor = systemInfo.getHardware().getProcessor(); + long[] prevTicks = processor.getSystemCpuLoadTicks(); + // 睡眠1s + TimeUnit.SECONDS.sleep(1); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; + long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + return 1.0-(idle * 1.0 / totalCpu); + } + + /** + * 获取内存使用率 + * @return + */ + public static double getMemInfo(){ + SystemInfo systemInfo = new SystemInfo(); + GlobalMemory memory = systemInfo.getHardware().getMemory(); + //总内存 + long totalByte = memory.getTotal(); + //剩余 + long acaliableByte = memory.getAvailable(); + return (totalByte-acaliableByte)*1.0/totalByte; + } + + /** + * 获取网络上传和下载 + * @return + */ + public static Map getNetworkInterfaces() { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + List beforeRecvNetworkIFs = hal.getNetworkIFs(); + NetworkIF beforeBet= beforeRecvNetworkIFs.get(beforeRecvNetworkIFs.size() - 1); + long beforeRecv = beforeBet.getBytesRecv(); + long beforeSend = beforeBet.getBytesSent(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error("[线程休眠失败] : {}", e.getMessage()); + } + List afterNetworkIFs = hal.getNetworkIFs(); + NetworkIF afterNet = afterNetworkIFs.get(afterNetworkIFs.size() - 1); + + HashMap map = new HashMap<>(); + // 速度单位: Mbps + map.put("in",formatUnits(afterNet.getBytesRecv()-beforeRecv, 1048576L)); + map.put("out",formatUnits(afterNet.getBytesSent()-beforeSend, 1048576L)); + return map; + } + + /** + * 获取带宽总值 + * @return + */ + public static long getNetworkTotal() { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + List recvNetworkIFs = hal.getNetworkIFs(); + NetworkIF networkIF= recvNetworkIFs.get(recvNetworkIFs.size() - 1); + + return networkIF.getSpeed()/1048576L/8L; + } + + public static double formatUnits(long value, long prefix) { + return (double)value / (double)prefix; + } + + /** + * 获取进程数 + * @return + */ + public static int getProcessesCount(){ + SystemInfo si = new SystemInfo(); + OperatingSystem os = si.getOperatingSystem(); + + int processCount = os.getProcessCount(); + return processCount; + } + + public static List> getDiskInfo() { + List> result = new ArrayList<>(); + + String osName = System.getProperty("os.name"); + List pathArray = new ArrayList<>(); + if (osName.startsWith("Mac OS")) { + // 苹果 + pathArray.add("/"); + } else if (osName.startsWith("Windows")) { + // windows + pathArray.add("C:"); + } else { + pathArray.add("/"); + pathArray.add("/home"); + } + for (String path : pathArray) { + Map infoMap = new HashMap<>(); + infoMap.put("path", path); + File partitionFile = new File(path); + // 单位: GB + infoMap.put("use", (partitionFile.getTotalSpace() - partitionFile.getFreeSpace())/1024/1024/1024D); + infoMap.put("free", partitionFile.getFreeSpace()/1024/1024/1024D); + result.add(infoMap); + } + return result; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/Test.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/Test.java new file mode 100644 index 0000000..5a5808e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/Test.java @@ -0,0 +1,35 @@ +package com.yfd.monitor.utils; + +import java.util.*; +import java.util.stream.Collectors; + +public class Test { + public static void main(String[] args) { + List> list = new ArrayList<>(); + Map map1 = new HashMap<>(); + map1.put("key", "value1"); + + Map map2 = new HashMap<>(); + map2.put("key", "value2"); + + Map map3 = new HashMap<>(); + map3.put("key", "value1"); + + list.add(map1); + list.add(map2); + list.add(map3); + + // 提取需要去重的字段,并去重 + Set uniqueKeys = new HashSet<>(); + List> uniqueList = list.stream() + .filter(map -> uniqueKeys.add(map.get("key"))) + .collect(Collectors.toList()); + + // 输出结果 + System.out.println("Original List:"); + list.forEach(System.out::println); + + System.out.println("\nUnique List:"); + uniqueList.forEach(System.out::println); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/UJson.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/UJson.java new file mode 100644 index 0000000..f0be38d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/UJson.java @@ -0,0 +1,149 @@ +package com.yfd.monitor.utils; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * @version 1.0 + * @date 2022/3/11 10:17 + */ +public class UJson { + + private static Logger logger = LoggerFactory.getLogger(UJson.class); + public static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + + static { + JSON_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); + } + + private ObjectNode node; + + public UJson(){ + this.node = JSON_MAPPER.createObjectNode(); + } + + public UJson(String json){ + if(StringUtils.isBlank(json)){ + this.node = JSON_MAPPER.createObjectNode(); + }else{ + try { + this.node = JSON_MAPPER.readValue(json, ObjectNode.class); + }catch (Exception e){ + logger.error(e.getMessage(), e); + this.node = JSON_MAPPER.createObjectNode(); + } + } + } + + public UJson(ObjectNode node){ + this.node = node; + } + + public String asText(String key){ + JsonNode jsonNode = node.get(key); + if(Objects.isNull(jsonNode)){ + return ""; + } + return jsonNode.asText(); + } + + public String asText(String key, String defaultVal){ + JsonNode jsonNode = node.get(key); + if(Objects.isNull(jsonNode)){ + return ""; + } + return jsonNode.asText(defaultVal); + } + + public UJson put(String key, String value){ + this.node.put(key, value); + return this; + } + + public UJson put(String key, Integer value){ + this.node.put(key, value); + return this; + } + + public static UJson json(){ + return new UJson(); + } + + public static UJson json(String json){ + return new UJson(json); + } + + public static T readJson(String json, Class clazz){ + if(StringUtils.isBlank(json)){ + return null; + } + try { + return JSON_MAPPER.readValue(json, clazz); + }catch (Exception e){ + logger.error(e.getMessage(), e); + return null; + } + } + + public static String writeJson(Object object) { + try{ + return JSON_MAPPER.writeValueAsString(object); + }catch (Exception e){ + logger.error(e.getMessage(), e); + return ""; + } + } + + @Override + public String toString() { + return node.toString(); + } + + public int asInt(String key, int defValue) { + JsonNode jsonNode = this.node.get(key); + if(Objects.isNull(jsonNode)){ + return defValue; + } + return jsonNode.asInt(defValue); + } + + public UJson getSon(String key) { + JsonNode sonNode = this.node.get(key); + if(Objects.isNull(sonNode)){ + return new UJson(); + } + return new UJson((ObjectNode) sonNode); + } + + public UJson set(String key, ObjectNode sonNode) { + this.node.set(key, sonNode); + return this; + } + + public UJson set(String key, UJson sonNode) { + this.node.set(key, sonNode.node); + return this; + } + + public Iterator> fields() { + return this.node.fields(); + } + + public ObjectNode getNode() { + return this.node; + } + + public UJson setAll(UJson json) { + this.node.setAll(json.node); + return this; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/redis/FastJsonRedisSerializer.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/redis/FastJsonRedisSerializer.java new file mode 100644 index 0000000..18fd471 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/redis/FastJsonRedisSerializer.java @@ -0,0 +1,45 @@ +package com.yfd.monitor.utils.redis; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * @description:使用fastjson实现redis的序列化 + * @date: 2020年5月6日 下午8:40:11 + */ +public class FastJsonRedisSerializer implements RedisSerializer { + + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + private Class clazz; + + public FastJsonRedisSerializer(Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.WritePairAsJavaBean).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException { + if (bytes == null || bytes.length <= 0) { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/utils/redis/RedisUtil.java b/riis-monitor/src/main/java/com/yfd/monitor/utils/redis/RedisUtil.java new file mode 100644 index 0000000..2da1b11 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/utils/redis/RedisUtil.java @@ -0,0 +1,45 @@ +package com.yfd.monitor.utils.redis; + +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Redis工具类 + * + * @date 2020年5月6日 下午8:27:29 + */ +@SuppressWarnings(value = {"rawtypes", "unchecked"}) +public class RedisUtil { + + /** + * 模糊查询 + * + * @param query 查询参数 + * @return + */ + public static List scan(RedisTemplate redisTemplate, String query) { + + Set resultKeys = (Set) redisTemplate.execute((RedisCallback>) connection -> { + ScanOptions scanOptions = ScanOptions.scanOptions().match("*" + query + "*").count(1000).build(); + Cursor scan = connection.scan(scanOptions); + Set keys = new HashSet<>(); + while (scan.hasNext()) { + byte[] next = scan.next(); + keys.add(new String(next)); + } + return keys; + }); + + return new ArrayList<>(resultKeys); + } +} + + + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/AudioBroadcastResult.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/AudioBroadcastResult.java new file mode 100644 index 0000000..653e49c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/AudioBroadcastResult.java @@ -0,0 +1,59 @@ +package com.yfd.monitor.vmanager.bean; + +/** + * @author lin + */ +public class AudioBroadcastResult { + /** + * 推流的各个方式流地址 + */ + private StreamContent streamInfo; + + /** + * 编码格式 + */ + private String codec; + + /** + * 向zlm推流的应用名 + */ + private String app; + + /** + * 向zlm推流的流ID + */ + private String stream; + + + public StreamContent getStreamInfo() { + return streamInfo; + } + + public void setStreamInfo(StreamContent streamInfo) { + this.streamInfo = streamInfo; + } + + public String getCodec() { + return codec; + } + + public void setCodec(String codec) { + this.codec = codec; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/BaseTree.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/BaseTree.java new file mode 100644 index 0000000..ae72f14 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/BaseTree.java @@ -0,0 +1,84 @@ +package com.yfd.monitor.vmanager.bean; + +import org.jetbrains.annotations.NotNull; + +import java.text.Collator; +import java.util.Comparator; + +public class BaseTree implements Comparable{ + private String id; + + private String deviceId; + private String pid; + private String name; + private boolean parent; + + private T basicData; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getPid() { + return pid; + } + + public void setPid(String pid) { + this.pid = pid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public T getBasicData() { + return basicData; + } + + public void setBasicData(T basicData) { + this.basicData = basicData; + } + + public boolean isParent() { + return parent; + } + + public void setParent(boolean parent) { + this.parent = parent; + } + + @Override + public int compareTo(@NotNull BaseTree treeNode) { + if (this.parent || treeNode.isParent()) { + if (!this.parent && !treeNode.isParent()) { + Comparator cmp = Collator.getInstance(java.util.Locale.CHINA); + return cmp.compare(treeNode.getName(), this.getName()); + }else { + if (this.isParent()) { + return 1; + }else { + return -1; + } + } + }else{ + Comparator cmp = Collator.getInstance(java.util.Locale.CHINA); + return cmp.compare(treeNode.getName(), this.getName()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/BatchGBStreamParam.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/BatchGBStreamParam.java new file mode 100644 index 0000000..87d8ad6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/BatchGBStreamParam.java @@ -0,0 +1,20 @@ +package com.yfd.monitor.vmanager.bean; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +@Schema(description = "多个推流信息") +public class BatchGBStreamParam { + @Schema(description = "推流信息列表") + private List gbStreams; + + public List getGbStreams() { + return gbStreams; + } + + public void setGbStreams(List gbStreams) { + this.gbStreams = gbStreams; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/DeferredResultEx.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/DeferredResultEx.java new file mode 100644 index 0000000..665cbc2 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/DeferredResultEx.java @@ -0,0 +1,31 @@ +package com.yfd.monitor.vmanager.bean; + +import org.springframework.web.context.request.async.DeferredResult; + +public class DeferredResultEx { + + private DeferredResult deferredResult; + + private DeferredResultFilter filter; + + public DeferredResultEx(DeferredResult result) { + this.deferredResult = result; + } + + + public DeferredResult getDeferredResult() { + return deferredResult; + } + + public void setDeferredResult(DeferredResult deferredResult) { + this.deferredResult = deferredResult; + } + + public DeferredResultFilter getFilter() { + return filter; + } + + public void setFilter(DeferredResultFilter filter) { + this.filter = filter; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/DeferredResultFilter.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/DeferredResultFilter.java new file mode 100644 index 0000000..72ce8d7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/DeferredResultFilter.java @@ -0,0 +1,6 @@ +package com.yfd.monitor.vmanager.bean; + +public interface DeferredResultFilter { + + Object handler(Object o); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ErrorCode.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ErrorCode.java new file mode 100644 index 0000000..8fa2c9b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ErrorCode.java @@ -0,0 +1,30 @@ +package com.yfd.monitor.vmanager.bean; + +/** + * 全局错误码 + */ +public enum ErrorCode { + SUCCESS(0, "成功"), + ERROR100(100, "失败"), + ERROR400(400, "参数不全或者错误"), + ERROR404(404, "资源未找到"), + ERROR403(403, "无权限操作"), + ERROR401(401, "请登录后重新请求"), + ERROR500(500, "系统异常"); + + private final int code; + private final String msg; + + ErrorCode(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/PlayTypeEnum.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/PlayTypeEnum.java new file mode 100644 index 0000000..fb574dd --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/PlayTypeEnum.java @@ -0,0 +1,23 @@ +package com.yfd.monitor.vmanager.bean; + +public enum PlayTypeEnum { + + PLAY("0", "直播"), + PLAY_BACK("1", "回放"); + + private String value; + private String name; + + PlayTypeEnum(String value, String name) { + this.value = value; + this.name = name; + } + + public String getValue() { + return value; + } + + public String getName() { + return name; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ResourceBaceInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ResourceBaceInfo.java new file mode 100644 index 0000000..759984e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ResourceBaceInfo.java @@ -0,0 +1,22 @@ +package com.yfd.monitor.vmanager.bean; + +public class ResourceBaceInfo { + private int total; + private int online; + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getOnline() { + return online; + } + + public void setOnline(int online) { + this.online = online; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ResourceInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ResourceInfo.java new file mode 100644 index 0000000..e13db85 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/ResourceInfo.java @@ -0,0 +1,41 @@ +package com.yfd.monitor.vmanager.bean; + +public class ResourceInfo { + + private ResourceBaceInfo device; + private ResourceBaceInfo channel; + private ResourceBaceInfo push; + private ResourceBaceInfo proxy; + + public ResourceBaceInfo getDevice() { + return device; + } + + public void setDevice(ResourceBaceInfo device) { + this.device = device; + } + + public ResourceBaceInfo getChannel() { + return channel; + } + + public void setChannel(ResourceBaceInfo channel) { + this.channel = channel; + } + + public ResourceBaceInfo getPush() { + return push; + } + + public void setPush(ResourceBaceInfo push) { + this.push = push; + } + + public ResourceBaceInfo getProxy() { + return proxy; + } + + public void setProxy(ResourceBaceInfo proxy) { + this.proxy = proxy; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/StreamContent.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/StreamContent.java new file mode 100644 index 0000000..3929612 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/StreamContent.java @@ -0,0 +1,417 @@ +package com.yfd.monitor.vmanager.bean; + +import com.yfd.monitor.common.StreamInfo; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "流信息") +public class StreamContent { + + @Schema(description = "应用名") + private String app; + + @Schema(description = "流ID") + private String stream; + + @Schema(description = "IP") + private String ip; + + @Schema(description = "HTTP-FLV流地址") + private String flv; + + @Schema(description = "HTTPS-FLV流地址") + private String https_flv; + + @Schema(description = "Websocket-FLV流地址") + private String ws_flv; + + @Schema(description = "Websockets-FLV流地址") + private String wss_flv; + + @Schema(description = "HTTP-FMP4流地址") + private String fmp4; + + @Schema(description = "HTTPS-FMP4流地址") + private String https_fmp4; + + @Schema(description = "Websocket-FMP4流地址") + private String ws_fmp4; + + @Schema(description = "Websockets-FMP4流地址") + private String wss_fmp4; + + @Schema(description = "HLS流地址") + private String hls; + + @Schema(description = "HTTPS-HLS流地址") + private String https_hls; + + @Schema(description = "Websocket-HLS流地址") + private String ws_hls; + + @Schema(description = "Websockets-HLS流地址") + private String wss_hls; + + @Schema(description = "HTTP-TS流地址") + private String ts; + + @Schema(description = "HTTPS-TS流地址") + private String https_ts; + + @Schema(description = "Websocket-TS流地址") + private String ws_ts; + + @Schema(description = "Websockets-TS流地址") + private String wss_ts; + + @Schema(description = "RTMP流地址") + private String rtmp; + + @Schema(description = "RTMPS流地址") + private String rtmps; + + @Schema(description = "RTSP流地址") + private String rtsp; + + @Schema(description = "RTSPS流地址") + private String rtsps; + + @Schema(description = "RTC流地址") + private String rtc; + + @Schema(description = "RTCS流地址") + private String rtcs; + + @Schema(description = "流媒体ID") + private String mediaServerId; + + @Schema(description = "流编码信息") + private Object tracks; + + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + private String endTime; + + private double progress; + public StreamContent(String fmp4,String ws_flv ) { + this.fmp4 = fmp4; + this.ws_flv = ws_flv; + } + public StreamContent(StreamInfo streamInfo) { + if (streamInfo == null) { + return; + } + this.app = streamInfo.getApp(); + this.stream = streamInfo.getStream(); + if (streamInfo.getFlv() != null) { + this.flv = streamInfo.getFlv().getUrl(); + } + if (streamInfo.getHttps_flv() != null) { + this.https_flv = streamInfo.getHttps_flv().getUrl(); + } + if (streamInfo.getWs_flv() != null) { + this.ws_flv = streamInfo.getWs_flv().getUrl(); + } + if (streamInfo.getWss_flv() != null) { + this.wss_flv = streamInfo.getWss_flv().getUrl(); + } + if (streamInfo.getFmp4() != null) { + this.fmp4 = streamInfo.getFmp4().getUrl(); + } + if (streamInfo.getHttps_fmp4() != null) { + this.https_fmp4 = streamInfo.getHttps_fmp4().getUrl(); + } + if (streamInfo.getWs_fmp4() != null) { + this.ws_fmp4 = streamInfo.getWs_fmp4().getUrl(); + } + if (streamInfo.getWss_fmp4() != null) { + this.wss_fmp4 = streamInfo.getWss_fmp4().getUrl(); + } + if (streamInfo.getHls() != null) { + this.hls = streamInfo.getHls().getUrl(); + } + if (streamInfo.getHttps_hls() != null) { + this.https_hls = streamInfo.getHttps_hls().getUrl(); + } + if (streamInfo.getWs_hls() != null) { + this.ws_hls = streamInfo.getWs_hls().getUrl(); + } + if (streamInfo.getWss_hls() != null) { + this.wss_hls = streamInfo.getWss_hls().getUrl(); + } + if (streamInfo.getTs() != null) { + this.ts = streamInfo.getTs().getUrl(); + } + if (streamInfo.getHttps_ts() != null) { + this.https_ts = streamInfo.getHttps_ts().getUrl(); + } + if (streamInfo.getWs_ts() != null) { + this.ws_ts = streamInfo.getWs_ts().getUrl(); + } + if (streamInfo.getRtmp() != null) { + this.rtmp = streamInfo.getRtmp().getUrl(); + } + if (streamInfo.getRtmps() != null) { + this.rtmps = streamInfo.getRtmps().getUrl(); + } + if (streamInfo.getRtsp() != null) { + this.rtsp = streamInfo.getRtsp().getUrl(); + } + if (streamInfo.getRtsps() != null) { + this.rtsps = streamInfo.getRtsps().getUrl(); + } + if (streamInfo.getRtc() != null) { + this.rtc = streamInfo.getRtc().getUrl(); + } + if (streamInfo.getRtcs() != null) { + this.rtcs = streamInfo.getRtcs().getUrl(); + } + + this.mediaServerId = streamInfo.getMediaServerId(); + this.tracks = streamInfo.getTracks(); + this.startTime = streamInfo.getStartTime(); + this.endTime = streamInfo.getEndTime(); + this.progress = streamInfo.getProgress(); + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getFlv() { + return flv; + } + + public void setFlv(String flv) { + this.flv = flv; + } + + public String getHttps_flv() { + return https_flv; + } + + public void setHttps_flv(String https_flv) { + this.https_flv = https_flv; + } + + public String getWs_flv() { + return ws_flv; + } + + public void setWs_flv(String ws_flv) { + this.ws_flv = ws_flv; + } + + public String getWss_flv() { + return wss_flv; + } + + public void setWss_flv(String wss_flv) { + this.wss_flv = wss_flv; + } + + public String getFmp4() { + return fmp4; + } + + public void setFmp4(String fmp4) { + this.fmp4 = fmp4; + } + + public String getHttps_fmp4() { + return https_fmp4; + } + + public void setHttps_fmp4(String https_fmp4) { + this.https_fmp4 = https_fmp4; + } + + public String getWs_fmp4() { + return ws_fmp4; + } + + public void setWs_fmp4(String ws_fmp4) { + this.ws_fmp4 = ws_fmp4; + } + + public String getWss_fmp4() { + return wss_fmp4; + } + + public void setWss_fmp4(String wss_fmp4) { + this.wss_fmp4 = wss_fmp4; + } + + public String getHls() { + return hls; + } + + public void setHls(String hls) { + this.hls = hls; + } + + public String getHttps_hls() { + return https_hls; + } + + public void setHttps_hls(String https_hls) { + this.https_hls = https_hls; + } + + public String getWs_hls() { + return ws_hls; + } + + public void setWs_hls(String ws_hls) { + this.ws_hls = ws_hls; + } + + public String getWss_hls() { + return wss_hls; + } + + public void setWss_hls(String wss_hls) { + this.wss_hls = wss_hls; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getHttps_ts() { + return https_ts; + } + + public void setHttps_ts(String https_ts) { + this.https_ts = https_ts; + } + + public String getWs_ts() { + return ws_ts; + } + + public void setWs_ts(String ws_ts) { + this.ws_ts = ws_ts; + } + + public String getWss_ts() { + return wss_ts; + } + + public void setWss_ts(String wss_ts) { + this.wss_ts = wss_ts; + } + + public String getRtmp() { + return rtmp; + } + + public void setRtmp(String rtmp) { + this.rtmp = rtmp; + } + + public String getRtmps() { + return rtmps; + } + + public void setRtmps(String rtmps) { + this.rtmps = rtmps; + } + + public String getRtsp() { + return rtsp; + } + + public void setRtsp(String rtsp) { + this.rtsp = rtsp; + } + + public String getRtsps() { + return rtsps; + } + + public void setRtsps(String rtsps) { + this.rtsps = rtsps; + } + + public String getRtc() { + return rtc; + } + + public void setRtc(String rtc) { + this.rtc = rtc; + } + + public String getRtcs() { + return rtcs; + } + + public void setRtcs(String rtcs) { + this.rtcs = rtcs; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public Object getTracks() { + return tracks; + } + + public void setTracks(Object tracks) { + this.tracks = tracks; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public double getProgress() { + return progress; + } + + public void setProgress(double progress) { + this.progress = progress; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/StreamPushExcelDto.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/StreamPushExcelDto.java new file mode 100644 index 0000000..3f05c0f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/StreamPushExcelDto.java @@ -0,0 +1,88 @@ +package com.yfd.monitor.vmanager.bean; + +import com.alibaba.excel.annotation.ExcelProperty; + +public class StreamPushExcelDto { + + @ExcelProperty("名称") + private String name; + + @ExcelProperty("应用名") + private String app; + + @ExcelProperty("流ID") + private String stream; + + @ExcelProperty("国标ID") + private String gbId; + + @ExcelProperty("平台ID") + private String platformId; + + @ExcelProperty("目录ID") + private String catalogId; + + @ExcelProperty("在线状态") + private boolean status; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getGbId() { + return gbId; + } + + public void setGbId(String gbId) { + this.gbId = gbId; + } + + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public boolean isStatus() { + return status; + } + + public boolean getStatus() { + return status; + } + + public void setStatus(boolean status) { + this.status = status; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/SystemConfigInfo.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/SystemConfigInfo.java new file mode 100644 index 0000000..9848586 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/SystemConfigInfo.java @@ -0,0 +1,47 @@ +package com.yfd.monitor.vmanager.bean; + +import com.yfd.monitor.common.VersionPo; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.VersionInfo; + +public class SystemConfigInfo { + + private int serverPort; + private SipConfig sip; + private UserSetting addOn; + private VersionPo version; + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public SipConfig getSip() { + return sip; + } + + public void setSip(SipConfig sip) { + this.sip = sip; + } + + public UserSetting getAddOn() { + return addOn; + } + + public void setAddOn(UserSetting addOn) { + this.addOn = addOn; + } + + public VersionPo getVersion() { + return version; + } + + public void setVersion(VersionPo version) { + this.version = version; + } +} + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/WVPResult.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/WVPResult.java new file mode 100644 index 0000000..debae1c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/bean/WVPResult.java @@ -0,0 +1,75 @@ +package com.yfd.monitor.vmanager.bean; + + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "统一返回结果") +public class WVPResult implements Cloneable{ + + public WVPResult() { + } + + public WVPResult(int code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + + @Schema(description = "错误码,0为成功") + private int code; + @Schema(description = "描述,错误时描述错误原因") + private String msg; + @Schema(description = "数据") + private T data; + + + public static WVPResult success(T t, String msg) { + return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t); + } + + public static WVPResult success() { + return new WVPResult<>(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), null); + } + + public static WVPResult success(T t) { + return success(t, ErrorCode.SUCCESS.getMsg()); + } + + public static WVPResult fail(int code, String msg) { + return new WVPResult<>(code, msg, null); + } + + public static WVPResult fail(ErrorCode errorCode) { + return fail(errorCode.getCode(), errorCode.getMsg()); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/MobilePosition/MobilePositionController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/MobilePosition/MobilePositionController.java new file mode 100644 index 0000000..b7b379a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/MobilePosition/MobilePositionController.java @@ -0,0 +1,144 @@ +package com.yfd.monitor.vmanager.gdw2019.MobilePosition; + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.MobilePosition; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.github.pagehelper.util.StringUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.List; +import java.util.UUID; + +/** + * 位置信息管理 + */ +@Tag(name = "位置信息管理") + +@RestController +@RequestMapping("/api/position") +public class MobilePositionController { + + private final static Logger logger = LoggerFactory.getLogger(MobilePositionController.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + + + + + /** + * 获取移动位置信息 + * @param deviceId 设备ID + * @return + */ + @Operation(summary = "获取移动位置信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/realtime/{deviceId}") + public DeferredResult realTimePosition(@PathVariable String deviceId) { + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId; + try { + cmder.mobilePostitionQuery(device, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取移动位置信息失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取移动位置信息: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult(5*1000L); + result.onTimeout(()->{ + logger.warn(String.format("获取移动位置信息超时")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 订阅位置信息 + * @param deviceId 设备ID + * @param expires 订阅超时时间 + * @param interval 上报时间间隔 + * @return true = 命令发送成功 + */ + @Operation(summary = "订阅位置信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "expires", description = "订阅超时时间", required = true) + @Parameter(name = "interval", description = "上报时间间隔", required = true) + @GetMapping("/subscribe/{deviceId}") + public void positionSubscribe(@PathVariable String deviceId, + @RequestParam String expires, + @RequestParam String interval) { + + if (StringUtil.isEmpty(interval)) { + interval = "5"; + } + Device device = storager.queryVideoDevice(deviceId); + device.setSubscribeCycleForMobilePosition(Integer.parseInt(expires)); + device.setMobilePositionSubmissionInterval(Integer.parseInt(interval)); + deviceService.updateDevice(device); + if (!deviceService.removeMobilePositionSubscribe(device)) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 数据位置信息格式处理 + * @param deviceId 设备ID + * @return true = 命令发送成功 + */ + @Operation(summary = "数据位置信息格式处理") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/transform/{deviceId}") + public void positionTransform(@PathVariable String deviceId) { + + Device device = deviceService.getDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); + } + boolean result = deviceChannelService.updateAllGps(device); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/SseController/SseController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/SseController/SseController.java new file mode 100644 index 0000000..457ab9b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/SseController/SseController.java @@ -0,0 +1,37 @@ +package com.yfd.monitor.vmanager.gdw2019.SseController; + +import com.yfd.monitor.gdw2019.event.alarm.AlarmEventListener; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +/** + * @description: SSE推送 + * + * @data: 2021-01-20 + */ +@Tag(name = "SSE推送") + +@Controller +@RequestMapping("/api") +public class SseController { + @Autowired + AlarmEventListener alarmEventListener; + + @GetMapping("/emit") + public SseEmitter emit(@RequestParam String browserId) { + final SseEmitter sseEmitter = new SseEmitter(0L); + try { + alarmEventListener.addSseEmitters(browserId, sseEmitter); + }catch (Exception e){ + sseEmitter.completeWithError(e); + } + return sseEmitter; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/alarm/AlarmController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/alarm/AlarmController.java new file mode 100644 index 0000000..dbbc52e --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/alarm/AlarmController.java @@ -0,0 +1,190 @@ +package com.yfd.monitor.vmanager.gdw2019.alarm; + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceAlarm; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommander; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.service.IDeviceAlarmService; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Tag(name = "报警信息管理") + +@RestController +@RequestMapping("/api/alarm") +public class AlarmController { + + private final static Logger logger = LoggerFactory.getLogger(AlarmController.class); + + @Autowired + private IDeviceAlarmService deviceAlarmService; + + @Autowired + private ISIPCommander commander; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private IVideoManagerStorage storage; + + + /** + * 删除报警 + * + * @param id 报警id + * @param deviceIds 多个设备id,逗号分隔 + * @param time 结束时间(这个时间之前的报警会被删除) + * @return + */ + @DeleteMapping("/delete") + @Operation(summary = "删除报警") + @Parameter(name = "id", description = "ID") + @Parameter(name = "deviceIds", description = "多个设备id,逗号分隔") + @Parameter(name = "time", description = "结束时间") + public Integer delete( + @RequestParam(required = false) Integer id, + @RequestParam(required = false) String deviceIds, + @RequestParam(required = false) String time + ) { + if (ObjectUtils.isEmpty(id)) { + id = null; + } + if (ObjectUtils.isEmpty(deviceIds)) { + deviceIds = null; + } + + if (ObjectUtils.isEmpty(time)) { + time = null; + }else if (!DateUtil.verification(time, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "time格式为" + DateUtil.PATTERN); + } + List deviceIdList = null; + if (deviceIds != null) { + String[] deviceIdArray = deviceIds.split(","); + deviceIdList = Arrays.asList(deviceIdArray); + } + + return deviceAlarmService.clearAlarmBeforeTime(id, deviceIdList, time); + } + + /** + * 测试向上级/设备发送模拟报警通知 + * + * @param deviceId 报警id + * @return + */ + @GetMapping("/test/notify/alarm") + @Operation(summary = "测试向上级/设备发送模拟报警通知") + @Parameter(name = "deviceId", description = "设备国标编号") + public void delete(@RequestParam String deviceId) { + Device device = storage.queryVideoDevice(deviceId); + ParentPlatform platform = storage.queryParentPlatByServerGBId(deviceId); + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setChannelId(deviceId); + deviceAlarm.setAlarmDescription("test"); + deviceAlarm.setAlarmMethod("1"); + deviceAlarm.setAlarmPriority("1"); + deviceAlarm.setAlarmTime(DateUtil.getNow()); + deviceAlarm.setAlarmType("1"); + deviceAlarm.setLongitude(115.33333); + deviceAlarm.setLatitude(39.33333); + + if (device != null && platform == null) { + + try { + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + + } + }else if (device == null && platform != null){ + try { + commanderForPlatform.sendAlarmMessage(platform, new ArrayList<>()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"无法确定" + deviceId + "是平台还是设备"); + } + + } + + /** + * 分页查询报警 + * + * @param deviceId 设备id + * @param page 当前页 + * @param count 每页查询数量 + * @param alarmPriority 报警级别 + * @param alarmMethod 报警方式 + * @param alarmType 报警类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + @Operation(summary = "分页查询报警") + @Parameter(name = "page",description = "当前页",required = true) + @Parameter(name = "count",description = "每页查询数量",required = true) + @Parameter(name = "deviceId",description = "设备id") + @Parameter(name = "alarmPriority",description = "查询内容") + @Parameter(name = "alarmMethod",description = "查询内容") + @Parameter(name = "alarmType",description = "每页查询数量") + @Parameter(name = "startTime",description = "开始时间") + @Parameter(name = "endTime",description = "结束时间") + @GetMapping("/all") + public PageInfo getAll( + @RequestParam int page, + @RequestParam int count, + @RequestParam(required = false) String deviceId, + @RequestParam(required = false) String alarmPriority, + @RequestParam(required = false) String alarmMethod, + @RequestParam(required = false) String alarmType, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime + ) { + if (ObjectUtils.isEmpty(alarmPriority)) { + alarmPriority = null; + } + if (ObjectUtils.isEmpty(alarmMethod)) { + alarmMethod = null; + } + if (ObjectUtils.isEmpty(alarmType)) { + alarmType = null; + } + + if (ObjectUtils.isEmpty(startTime)) { + startTime = null; + }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + + if (ObjectUtils.isEmpty(endTime)) { + endTime = null; + }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + + return deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod, + alarmType, startTime, endTime); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceConfig.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceConfig.java new file mode 100644 index 0000000..528ae2f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceConfig.java @@ -0,0 +1,156 @@ +/** + * 设备设置命令API接口 + * + * + * @date 2021年2月2日 + */ + +package com.yfd.monitor.vmanager.gdw2019.device; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.UUID; + +@Tag(name = "国标设备配置") +@RestController +@RequestMapping("/api/device/config") +public class DeviceConfig { + + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + /** + * 看守位控制命令API接口 + * @param deviceId 设备ID + * @param channelId 通道ID + * @param name 名称 + * @param expiration 到期时间 + * @param heartBeatInterval 心跳间隔 + * @param heartBeatCount 心跳计数 + * @return + */ + @GetMapping("/basicParam/{deviceId}") + @Operation(summary = "基本配置设置命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "name", description = "名称") + @Parameter(name = "expiration", description = "到期时间") + @Parameter(name = "heartBeatInterval", description = "心跳间隔") + @Parameter(name = "heartBeatCount", description = "心跳计数") + public DeferredResult homePositionApi(@PathVariable String deviceId, + String channelId, + @RequestParam(required = false) String name, + @RequestParam(required = false) String expiration, + @RequestParam(required = false) String heartBeatInterval, + @RequestParam(required = false) String heartBeatCount) { + if (logger.isDebugEnabled()) { + logger.debug("报警复位API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId; + try { + cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("设备配置操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 设备配置: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult(3 * 1000L); + result.onTimeout(() -> { + logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("Status", "Timeout"); + json.put("Description", "设备配置操作超时, 设备未返回应答指令"); + msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 设备配置查询请求API接口 + * @param deviceId 设备ID + * @param configType 配置类型 + * @param channelId 通道ID + * @return + */ + @Operation(summary = "设备配置查询请求") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "configType", description = "配置类型") + @GetMapping("/query/{deviceId}/{configType}") + public DeferredResult configDownloadApi(@PathVariable String deviceId, + @PathVariable String configType, + @RequestParam(required = false) String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("设备状态查询API调用"); + } + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + String uuid = UUID.randomUUID().toString(); + Device device = storager.queryVideoDevice(deviceId); + try { + cmder.deviceConfigQuery(device, channelId, configType, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备配置失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备配置: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult (3 * 1000L); + result.onTimeout(()->{ + logger.warn(String.format("获取设备配置超时")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceControl.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceControl.java new file mode 100644 index 0000000..c2f28b6 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceControl.java @@ -0,0 +1,401 @@ +/** + * 设备控制命令API接口 + * + * + * @date 2021年2月1日 + */ + +package com.yfd.monitor.vmanager.gdw2019.device; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.UUID; + +@Tag(name = "国标设备控制") + +@RestController +@RequestMapping("/api/device/control") +public class DeviceControl { + + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + /** + * 远程启动控制命令API接口 + * + * @param deviceId 设备ID + */ + @Operation(summary = "远程启动控制命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/teleboot/{deviceId}") + public void teleBootApi(@PathVariable String deviceId) { + if (logger.isDebugEnabled()) { + logger.debug("设备远程启动API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + try { + cmder.teleBootCmd(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 远程启动: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + /** + * 录像控制命令API接口 + * + * @param deviceId 设备ID + * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 + * @param channelId 通道编码(可选) + */ + @Operation(summary = "录像控制") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "recordCmdStr", description = "命令, 可选值:Record(手动录像),StopRecord(停止手动录像)", required = true) + @GetMapping("/record/{deviceId}/{recordCmdStr}") + public DeferredResult> recordApi(@PathVariable String deviceId, + @PathVariable String recordCmdStr, String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("开始/停止录像API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; + DeferredResult> result = new DeferredResult>(3 * 1000L); + result.onTimeout(() -> { + logger.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeAllResult(msg); + }); + if (resultHolder.exist(key, null)){ + return result; + } + resultHolder.put(key, uuid, result); + try { + cmder.recordCmd(device, channelId, recordCmdStr, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeAllResult(msg); + },null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + return result; + } + + /** + * 报警布防/撤防命令API接口 + * + * @param deviceId 设备ID + * @param guardCmdStr SetGuard:布防,ResetGuard:撤防 + */ + @Operation(summary = "布防/撤防命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true) + @GetMapping("/guard/{deviceId}/{guardCmdStr}") + public DeferredResult guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { + if (logger.isDebugEnabled()) { + logger.debug("布防/撤防API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + deviceId; + String uuid =UUID.randomUUID().toString(); + try { + cmder.guardCmd(device, guardCmdStr, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + },null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage()); + } + DeferredResult result = new DeferredResult<>(3 * 1000L); + resultHolder.put(key, uuid, result); + result.onTimeout(() -> { + logger.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + + return result; + } + + /** + * 报警复位API接口 + * + * @param deviceId 设备ID + * @param alarmMethod 报警方式(可选) + * @param alarmType 报警类型(可选) + */ + @Operation(summary = "报警复位") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "alarmMethod", description = "报警方式") + @Parameter(name = "alarmType", description = "报警类型") + @GetMapping("/reset_alarm/{deviceId}") + public DeferredResult> resetAlarmApi(@PathVariable String deviceId, String channelId, + @RequestParam(required = false) String alarmMethod, + @RequestParam(required = false) String alarmType) { + if (logger.isDebugEnabled()) { + logger.debug("报警复位API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; + try { + cmder.alarmCmd(device, alarmMethod, alarmType, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + },null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 报警复位: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult> result = new DeferredResult>(3 * 1000L); + result.onTimeout(() -> { + logger.warn(String.format("报警复位操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 强制关键帧API接口 + * + * @param deviceId 设备ID + * @param channelId 通道ID + */ + @Operation(summary = "强制关键帧") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") + @GetMapping("/i_frame/{deviceId}") + public JSONObject iFrame(@PathVariable String deviceId, + @RequestParam(required = false) String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("强制关键帧API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + try { + cmder.iFrameCmd(device, channelId); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("ChannelID", channelId); + json.put("Result", "OK"); + return json; + } + + /** + * 看守位控制命令API接口 + * + * @param deviceId 设备ID + * @param enabled 看守位使能1:开启,0:关闭 + * @param resetTime 自动归位时间间隔(可选) + * @param presetIndex 调用预置位编号(可选) + * @param channelId 通道编码(可选) + */ + @Operation(summary = "看守位控制") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "enabled", description = "是否开启看守位 1:开启,0:关闭", required = true) + @Parameter(name = "presetIndex", description = "调用预置位编号") + @Parameter(name = "resetTime", description = "自动归位时间间隔") + @GetMapping("/home_position/{deviceId}/{enabled}") + public DeferredResult homePositionApi(@PathVariable String deviceId, + @PathVariable String enabled, + @RequestParam(required = false) String resetTime, + @RequestParam(required = false) String presetIndex, + String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("报警复位API调用"); + } + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + String uuid = UUID.randomUUID().toString(); + Device device = storager.queryVideoDevice(deviceId); + try { + cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + },null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 看守位控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult<>(3 * 1000L); + result.onTimeout(() -> { + logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("Status", "Timeout"); + json.put("Description", "看守位控制操作超时, 设备未返回应答指令"); + msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(key, uuid, result); + return result; + } + + /** + * 拉框放大 + * @param deviceId 设备id + * @param channelId 通道id + * @param length 播放窗口长度像素值 + * @param width 播放窗口宽度像素值 + * @param midpointx 拉框中心的横轴坐标像素值 + * @param midpointy 拉框中心的纵轴坐标像素值 + * @param lengthx 拉框长度像素值 + * @param lengthy 拉框宽度像素值 + * @return + */ + @Operation(summary = "拉框放大") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "length", description = "播放窗口长度像素值", required = true) + @Parameter(name = "midpointx", description = "拉框中心的横轴坐标像素值", required = true) + @Parameter(name = "midpointy", description = "拉框中心的纵轴坐标像素值", required = true) + @Parameter(name = "lengthx", description = "拉框长度像素值", required = true) + @Parameter(name = "lengthy", description = "lengthy", required = true) + @GetMapping("drag_zoom/zoom_in") + public void dragZoomIn(@RequestParam String deviceId, + @RequestParam(required = false) String channelId, + @RequestParam int length, + @RequestParam int width, + @RequestParam int midpointx, + @RequestParam int midpointy, + @RequestParam int lengthx, + @RequestParam int lengthy) throws RuntimeException { + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备拉框放大 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy)); + } + Device device = storager.queryVideoDevice(deviceId); + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("" + length+ "\r\n"); + cmdXml.append("" + width+ "\r\n"); + cmdXml.append("" + midpointx+ "\r\n"); + cmdXml.append("" + midpointy+ "\r\n"); + cmdXml.append("" + lengthx+ "\r\n"); + cmdXml.append("" + lengthy+ "\r\n"); + cmdXml.append("\r\n"); + try { + cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 拉框放大: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + /** + * 拉框缩小 + * @param deviceId 设备id + * @param channelId 通道id + * @param length 播放窗口长度像素值 + * @param width 播放窗口宽度像素值 + * @param midpointx 拉框中心的横轴坐标像素值 + * @param midpointy 拉框中心的纵轴坐标像素值 + * @param lengthx 拉框长度像素值 + * @param lengthy 拉框宽度像素值 + * @return + */ + @Operation(summary = "拉框放大") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") + @Parameter(name = "length", description = "播放窗口长度像素值", required = true) + @Parameter(name = "width", description = "拉框中心的横轴坐标像素值", required = true) + @Parameter(name = "midpointx", description = "拉框中心的横轴坐标像素值", required = true) + @Parameter(name = "midpointy", description = "拉框中心的纵轴坐标像素值", required = true) + @Parameter(name = "lengthx", description = "拉框长度像素值", required = true) + @Parameter(name = "lengthy", description = "拉框宽度像素值", required = true) + @GetMapping("/drag_zoom/zoom_out") + public void dragZoomOut(@RequestParam String deviceId, + @RequestParam(required = false) String channelId, + @RequestParam int length, + @RequestParam int width, + @RequestParam int midpointx, + @RequestParam int midpointy, + @RequestParam int lengthx, + @RequestParam int lengthy){ + + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备拉框缩小 API调用,deviceId:%s ,channelId:%s ,length:%d ,width:%d ,midpointx:%d ,midpointy:%d ,lengthx:%d ,lengthy:%d",deviceId, channelId, length, width, midpointx, midpointy,lengthx, lengthy)); + } + Device device = storager.queryVideoDevice(deviceId); + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("" + length+ "\r\n"); + cmdXml.append("" + width+ "\r\n"); + cmdXml.append("" + midpointx+ "\r\n"); + cmdXml.append("" + midpointy+ "\r\n"); + cmdXml.append("" + lengthx+ "\r\n"); + cmdXml.append("" + lengthy+ "\r\n"); + cmdXml.append("\r\n"); + try { + cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 拉框缩小: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceQuery.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceQuery.java new file mode 100644 index 0000000..aefbf74 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/device/DeviceQuery.java @@ -0,0 +1,567 @@ +package com.yfd.monitor.vmanager.gdw2019.device; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.SyncStatus; +import com.yfd.monitor.gdw2019.task.ISubscribeTask; +import com.yfd.monitor.gdw2019.task.impl.CatalogSubscribeTask; +import com.yfd.monitor.gdw2019.task.impl.MobilePositionSubscribeTask; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.service.IDeviceChannelService; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.BaseTree; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.WVPResult; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.compress.utils.IOUtils; +import org.apache.ibatis.annotations.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.servlet.http.HttpServletResponse; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.text.ParseException; +import java.util.*; + +@Tag(name = "国标设备查询", description = "国标设备查询") +@SuppressWarnings("rawtypes") + +@RestController +@RequestMapping("/api/device/query") +public class DeviceQuery { + + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SIPCommander cmder; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private DynamicTask dynamicTask; + + /** + * 使用ID查询国标设备 + * @param deviceId 国标ID + * @return 国标设备 + */ + @Operation(summary = "查询国标设备") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/devices/{deviceId}") + public Device devices(@PathVariable String deviceId){ + + return storager.queryVideoDevice(deviceId); + } + + /** + * 分页查询国标设备 + * @param page 当前页 + * @param count 每页查询数量 + * @return 分页国标列表 + */ + @Operation(summary = "分页查询国标设备") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @GetMapping("/devices") + @Options() + public PageInfo devices(int page, int count){ +// if (page == null) page = 0; +// if (count == null) count = 20; + return storager.queryVideoDeviceList(page, count,null); + } + + /** + * 分页查询通道数 + * + * @param deviceId 设备id + * @param page 当前页 + * @param count 每页条数 + * @param query 查询内容 + * @param online 是否在线 在线 true / 离线 false + * @param channelType 设备 false/子目录 true + * @param catalogUnderDevice 是否直属与设备的目录 + * @return 通道列表 + */ + @GetMapping("/devices/{deviceId}/channels") + @Operation(summary = "分页查询通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "channelType", description = "设备/子目录-> false/true") + @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录") + public PageInfo channels(@PathVariable String deviceId, + int page, int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean channelType, + @RequestParam(required = false) Boolean catalogUnderDevice) { + if (ObjectUtils.isEmpty(query)) { + query = null; + } + + return storager.queryChannelsByDeviceId(deviceId, query, channelType, online, catalogUnderDevice, page, count); + } + + /** + * 同步设备通道 + * @param deviceId 设备id + * @return + */ + @Operation(summary = "同步设备通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/devices/{deviceId}/sync") + public WVPResult devicesSync(@PathVariable String deviceId){ + + if (logger.isDebugEnabled()) { + logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); + } + Device device = storager.queryVideoDevice(deviceId); + boolean status = deviceService.isSyncRunning(deviceId); + // 已存在则返回进度 + if (status) { + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); + return WVPResult.success(channelSyncStatus); + } + deviceService.sync(device); + + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(0); + wvpResult.setMsg("开始同步"); + return wvpResult; + } + + /** + * 移除设备 + * @param deviceId 设备id + * @return + */ + @Operation(summary = "移除设备") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @DeleteMapping("/devices/{deviceId}/delete") + public String delete(@PathVariable String deviceId){ + + if (logger.isDebugEnabled()) { + logger.debug("设备信息删除API调用,deviceId:" + deviceId); + } + + // 清除redis记录 + boolean isSuccess = deviceService.delete(deviceId); + if (isSuccess) { + redisCatchStorage.clearCatchByDeviceId(deviceId); + // 停止此设备的订阅更新 + Set allKeys = dynamicTask.getAllKeys(); + for (String key : allKeys) { + if (key.startsWith(deviceId)) { + Runnable runnable = dynamicTask.get(key); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + dynamicTask.stop(key); + } + } + JSONObject json = new JSONObject(); + json.put("deviceId", deviceId); + return json.toString(); + } else { + logger.warn("设备信息删除API调用失败!"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!"); + } + } + + /** + * 分页查询子目录通道 + * @param deviceId 通道id + * @param channelId 通道id + * @param page 当前页 + * @param count 每页条数 + * @param query 查询内容 + * @param online 是否在线 + * @param channelType 通道类型 + * @return 子通道列表 + */ + @Operation(summary = "分页查询子目录通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "channelType", description = "设备/子目录-> false/true") + @GetMapping("/sub_channels/{deviceId}/{channelId}/channels") + public PageInfo subChannels(@PathVariable String deviceId, + @PathVariable String channelId, + int page, + int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean channelType){ + + DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); + if (deviceChannel == null) { + PageInfo deviceChannelPageResult = new PageInfo<>(); + return deviceChannelPageResult; + } + + return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); + } + + /** + * 更新通道信息 + * @param deviceId 设备id + * @param channel 通道 + * @return + */ + @Operation(summary = "更新通道信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channel", description = "通道信息", required = true) + @PostMapping("/channel/update/{deviceId}") + public void updateChannel(@PathVariable String deviceId,DeviceChannel channel){ + deviceChannelService.updateChannel(deviceId, channel); + } + + /** + * 修改数据流传输模式 + * @param deviceId 设备id + * @param streamMode 数据流传输模式 + * @return + */ + @Operation(summary = "修改数据流传输模式") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" + + "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true) + @PostMapping("/transport/{deviceId}/{streamMode}") + public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ + Device device = deviceService.getDevice(deviceId); + device.setStreamMode(streamMode); + deviceService.updateCustomDevice(device); + } + + /** + * 添加设备信息 + * @param device 设备信息 + * @return + */ + @Operation(summary = "添加设备信息") + @Parameter(name = "device", description = "设备", required = true) + @PostMapping("/device/add/") + public void addDevice(Device device){ + + if (device == null || device.getDeviceId() == null) { + throw new ControllerException(ErrorCode.ERROR400); + } + + // 查看deviceId是否存在 + boolean exist = deviceService.isExist(device.getDeviceId()); + if (exist) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备编号已存在"); + } + deviceService.addDevice(device); + } + + /** + * 更新设备信息 + * @param device 设备信息 + * @return + */ + @Operation(summary = "更新设备信息") + @Parameter(name = "device", description = "设备", required = true) + @PostMapping("/device/update/") + public void updateDevice(Device device){ + + if (device != null && device.getDeviceId() != null) { + deviceService.updateCustomDevice(device); + } + } + + /** + * 设备状态查询请求API接口 + * + * @param deviceId 设备id + */ + @Operation(summary = "设备状态查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/devices/{deviceId}/status") + public DeferredResult> deviceStatusApi(@PathVariable String deviceId) { + if (logger.isDebugEnabled()) { + logger.debug("设备状态查询API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; + DeferredResult> result = new DeferredResult>(2*1000L); + if(device == null) { + result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK)); + return result; + } + try { + cmder.deviceStatusQuery(device, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + result.onTimeout(()->{ + logger.warn(String.format("获取设备状态超时")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("Timeout. Device did not response to this command."); + resultHolder.invokeResult(msg); + }); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, uuid, result); + return result; + } + + /** + * 设备报警查询请求API接口 + * @param deviceId 设备id + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @Operation(summary = "设备状态查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "startPriority", description = "报警起始级别") + @Parameter(name = "endPriority", description = "报警终止级别") + @Parameter(name = "alarmMethod", description = "报警方式条件") + @Parameter(name = "alarmType", description = "报警类型") + @Parameter(name = "startTime", description = "报警发生起始时间") + @Parameter(name = "endTime", description = "报警发生终止时间") + @GetMapping("/alarm/{deviceId}") + public DeferredResult> alarmApi(@PathVariable String deviceId, + @RequestParam(required = false) String startPriority, + @RequestParam(required = false) String endPriority, + @RequestParam(required = false) String alarmMethod, + @RequestParam(required = false) String alarmType, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime) { + if (logger.isDebugEnabled()) { + logger.debug("设备报警查询API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId; + String uuid = UUID.randomUUID().toString(); + try { + cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult> result = new DeferredResult> (3 * 1000L); + result.onTimeout(()->{ + logger.warn(String.format("设备报警查询超时")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("设备报警查询超时"); + resultHolder.invokeResult(msg); + }); + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, uuid, result); + return result; + } + + + @GetMapping("/{deviceId}/sync_status") + @Operation(summary = "获取通道同步进度") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + public WVPResult getSyncStatus(@PathVariable String deviceId) { + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); + WVPResult wvpResult = new WVPResult<>(); + if (channelSyncStatus == null) { + wvpResult.setCode(-1); + wvpResult.setMsg("同步尚未开始"); + }else { + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + wvpResult.setData(channelSyncStatus); + if (channelSyncStatus.getErrorMsg() != null) { + wvpResult.setMsg(channelSyncStatus.getErrorMsg()); + } + } + return wvpResult; + } + + @GetMapping("/{deviceId}/subscribe_info") + @Operation(summary = "获取设备的订阅状态") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + public WVPResult> getSubscribeInfo(@PathVariable String deviceId) { + Set allKeys = dynamicTask.getAllKeys(); + Map dialogStateMap = new HashMap<>(); + for (String key : allKeys) { + if (key.startsWith(deviceId)) { + ISubscribeTask subscribeTask = (ISubscribeTask)dynamicTask.get(key); + if (subscribeTask instanceof CatalogSubscribeTask) { + dialogStateMap.put("catalog", 1); + }else if (subscribeTask instanceof MobilePositionSubscribeTask) { + dialogStateMap.put("mobilePosition", 1); + } + } + } + WVPResult> wvpResult = new WVPResult<>(); + wvpResult.setCode(0); + wvpResult.setData(dialogStateMap); + return wvpResult; + } + + @GetMapping("/snap/{deviceId}/{channelId}") + @Operation(summary = "请求截图") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) { + + try { + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath()); + resp.setContentType(MediaType.IMAGE_PNG_VALUE); + IOUtils.copy(in, resp.getOutputStream()); + } catch (IOException e) { + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * 查询国标树 + * @param deviceId 设备ID + * @param parentId 父ID + * @param page 当前页 + * @param count 每页条数 + * @return 国标设备 + */ + @Operation(summary = "查询国标树") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "parentId", description = "父级国标编号") + @Parameter(name = "onlyCatalog", description = "只获取目录") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + @GetMapping("/tree/{deviceId}") + public ResponseEntity getTree(@PathVariable String deviceId, + @RequestParam(required = false) String parentId, + @RequestParam(required = false) Boolean onlyCatalog, + int page, int count){ + + + if (page <= 0) { + page = 1; + } + if (onlyCatalog == null) { + onlyCatalog = false; + } + + List> treeData = deviceService.queryVideoDeviceTree(deviceId, parentId, onlyCatalog); + if (treeData == null || (page - 1) * count > treeData.size()) { + PageInfo> pageInfo = new PageInfo<>(); + pageInfo.setPageNum(page); + pageInfo.setTotal(treeData == null? 0 : treeData.size()); + pageInfo.setSize(0); + pageInfo.setList(new ArrayList<>()); + return new ResponseEntity<>(pageInfo,HttpStatus.OK); + } + + int toIndex = Math.min(page * count, treeData.size()); + // 处理分页 + List> trees = treeData.subList((page - 1) * count, toIndex); + PageInfo> pageInfo = new PageInfo<>(); + pageInfo.setPageNum(page); + pageInfo.setTotal(treeData.size()); + pageInfo.setSize(trees.size()); + pageInfo.setList(trees); + + return new ResponseEntity<>(pageInfo,HttpStatus.OK); + } + + /** + * 查询国标树下的通道 + * @param deviceId 设备ID + * @param parentId 父ID + * @param page 当前页 + * @param count 每页条数 + * @return 国标设备 + */ + @Operation(summary = "查询国标树下的通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "parentId", description = "父级国标编号") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + @GetMapping("/tree/channel/{deviceId}") + public ResponseEntity getChannelInTreeNode(@PathVariable String deviceId, @RequestParam(required = false) String parentId, int page, int count){ + + if (page <= 0) { + page = 1; + } + + List treeData = deviceService.queryVideoDeviceInTreeNode(deviceId, parentId); + if (treeData == null || (page - 1) * count > treeData.size()) { + PageInfo> pageInfo = new PageInfo<>(); + pageInfo.setPageNum(page); + pageInfo.setTotal(treeData == null? 0 : treeData.size()); + pageInfo.setSize(0); + pageInfo.setList(new ArrayList<>()); + return new ResponseEntity<>(pageInfo,HttpStatus.OK); + } + + int toIndex = Math.min(page * count, treeData.size()); + // 处理分页 + List trees = treeData.subList((page - 1) * count, toIndex); + PageInfo pageInfo = new PageInfo<>(); + pageInfo.setPageNum(page); + pageInfo.setTotal(treeData.size()); + pageInfo.setSize(trees.size()); + pageInfo.setList(trees); + + return new ResponseEntity<>(pageInfo,HttpStatus.OK); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/gbStream/GbStreamController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/gbStream/GbStreamController.java new file mode 100644 index 0000000..db8d7f1 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/gbStream/GbStreamController.java @@ -0,0 +1,110 @@ +package com.yfd.monitor.vmanager.gdw2019.gbStream; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.service.IGbStreamService; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.gdw2019.gbStream.bean.GbStreamParam; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "视频流关联到级联平台") + +@RestController +@RequestMapping("/api/gbStream") +public class GbStreamController { + + private final static Logger logger = LoggerFactory.getLogger(GbStreamController.class); + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private IVideoManagerStorage storager; + + + /** + * 查询国标通道 + * @param page 当前页 + * @param count 每页条数 + * @param platformId 平台ID + * @return + */ + @Operation(summary = "查询国标通道") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + @Parameter(name = "platformId", description = "平台ID", required = true) + @Parameter(name = "catalogId", description = "目录ID") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "mediaServerId", description = "流媒体ID") + @GetMapping(value = "/list") + @ResponseBody + public PageInfo list(@RequestParam(required = true)Integer page, + @RequestParam(required = true)Integer count, + @RequestParam(required = true)String platformId, + @RequestParam(required = false)String catalogId, + @RequestParam(required = false)String query, + @RequestParam(required = false)String mediaServerId){ + if (ObjectUtils.isEmpty(catalogId)) { + catalogId = null; + } + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } + + // catalogId 为null 查询未在平台下分配的数据 + // catalogId 不为null 查询平台下这个,目录下的通道 + return gbStreamService.getAll(page, count, platformId, catalogId, query, mediaServerId); + } + + + /** + * 移除国标关联 + * @param gbStreamParam + * @return + */ + @Operation(summary = "移除国标关联") + @DeleteMapping(value = "/del") + @ResponseBody + public void del(@RequestBody GbStreamParam gbStreamParam){ + + if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) { + if (gbStreamParam.isAll()) { + gbStreamService.delAllPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); + } + }else { + gbStreamService.delPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getGbStreams()); + } + + } + + /** + * 保存国标关联 + * @param gbStreamParam + * @return + */ + @Operation(summary = "保存国标关联") + @PostMapping(value = "/add") + @ResponseBody + public void add(@RequestBody GbStreamParam gbStreamParam){ + if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) { + if (gbStreamParam.isAll()) { + List allGBChannels = gbStreamService.getAllGBChannels(gbStreamParam.getPlatformId()); + gbStreamService.addPlatformInfo(allGBChannels, gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); + } + }else { + gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/gbStream/bean/GbStreamParam.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/gbStream/bean/GbStreamParam.java new file mode 100644 index 0000000..64816d4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/gbStream/bean/GbStreamParam.java @@ -0,0 +1,54 @@ +package com.yfd.monitor.vmanager.gdw2019.gbStream.bean; + +import com.yfd.monitor.gdw2019.bean.GbStream; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +@Schema(description = "国标关联参数") +public class GbStreamParam { + + @Schema(description = "平台ID") + private String platformId; + + @Schema(description = "目录ID") + private String catalogId; + + @Schema(description = "关联所有通道") + private boolean all; + + @Schema(description = "流国标信息列表") + private List gbStreams; + + public String getPlatformId() { + return platformId; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public List getGbStreams() { + return gbStreams; + } + + public void setGbStreams(List gbStreams) { + this.gbStreams = gbStreams; + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/media/MediaController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/media/MediaController.java new file mode 100644 index 0000000..743de3a --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/media/MediaController.java @@ -0,0 +1,117 @@ +package com.yfd.monitor.vmanager.gdw2019.media; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.security.SecurityUtils; +import com.yfd.monitor.conf.security.dto.LoginUser; +import com.yfd.monitor.media.zlm.dto.StreamAuthorityInfo; +import com.yfd.monitor.service.IMediaService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + + +@Tag(name = "媒体流相关") +@Controller + +@RequestMapping(value = "/api/media") +public class MediaController { + + private final static Logger logger = LoggerFactory.getLogger(MediaController.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaService mediaService; + @Autowired + private IStreamProxyService streamProxyService; + + + /** + * 根据应用名和流id获取播放地址 + * @param app 应用名 + * @param stream 流id + * @return + */ + @Operation(summary = "根据应用名和流id获取播放地址") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + @Parameter(name = "mediaServerId", description = "媒体服务器id") + @Parameter(name = "callId", description = "推流时携带的自定义鉴权ID") + @Parameter(name = "useSourceIpAsStreamIp", description = "是否使用请求IP作为返回的地址IP") + @GetMapping(value = "/stream_info_by_app_and_stream") + @ResponseBody + public StreamContent getStreamInfoByAppAndStream(HttpServletRequest request, @RequestParam String app, + @RequestParam String stream, + @RequestParam(required = false) String mediaServerId, + @RequestParam(required = false) String callId, + @RequestParam(required = false) Boolean useSourceIpAsStreamIp){ + boolean authority = false; + if (callId != null) { + // 权限校验 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); + if (streamAuthorityInfo != null + && streamAuthorityInfo.getCallId() != null + && streamAuthorityInfo.getCallId().equals(callId)) { + authority = true; + }else { + throw new ControllerException(ErrorCode.ERROR400); + } + }else { + // 是否登陆用户, 登陆用户返回完整信息 + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo!= null) { + authority = true; + } + } + + StreamInfo streamInfo; + + if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { + String host = request.getHeader("Host"); + String localAddr = host.split(":")[0]; + logger.info("使用{}作为返回流的ip", localAddr); + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); + }else { + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + } + + if (streamInfo != null){ + return new StreamContent(streamInfo); + }else { + //获取流失败,重启拉流后重试一次 + streamProxyService.stop(app,stream); + boolean start = streamProxyService.start(app, stream); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error("[线程休眠失败], {}", e.getMessage()); + } + if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { + String host = request.getHeader("Host"); + String localAddr = host.split(":")[0]; + logger.info("使用{}作为返回流的ip", localAddr); + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); + }else { + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + } + if (streamInfo != null){ + return new StreamContent(streamInfo); + }else { + throw new ControllerException(ErrorCode.ERROR100); + } + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/PlatformController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/PlatformController.java new file mode 100644 index 0000000..ee4d76c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/PlatformController.java @@ -0,0 +1,562 @@ +package com.yfd.monitor.vmanager.gdw2019.platform; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.DynamicTask; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.gdw2019.bean.ParentPlatformCatch; +import com.yfd.monitor.gdw2019.bean.PlatformCatalog; +import com.yfd.monitor.gdw2019.bean.SubscribeHolder; +import com.yfd.monitor.gdw2019.transmit.cmd.ISIPCommanderForPlatform; +import com.yfd.monitor.service.*; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.ChannelReduce; +import com.yfd.monitor.vmanager.gdw2019.platform.bean.UpdateChannelParam; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import com.yfd.monitor.conf.SipConfig; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.List; + +/** + * 级联平台管理 + */ +@Tag(name = "级联平台管理") + +@RestController +@RequestMapping("/api/platform") +public class PlatformController { + + private final static Logger logger = LoggerFactory.getLogger(PlatformController.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IPlatformChannelService platformChannelService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private IPlatformService platformService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IGbStreamService gbStreamService; + + /** + * 获取国标服务的配置 + * + * @return + */ + @Operation(summary = "获取国标服务的配置") + @GetMapping("/server_config") + public JSONObject serverConfig() { + JSONObject result = new JSONObject(); + result.put("deviceIp", sipConfig.getIp()); + result.put("devicePort", sipConfig.getPort()); + result.put("username", sipConfig.getId()); + result.put("password", sipConfig.getPassword()); + return result; + } + + /** + * 获取级联服务器信息 + * + * @return + */ + @Operation(summary = "获取级联服务器信息") + @Parameter(name = "id", description = "平台国标编号", required = true) + @GetMapping("/info/{id}") + public ParentPlatform getPlatform(@PathVariable String id) { + ParentPlatform parentPlatform = platformService.queryPlatformByServerGBId(id); + if (parentPlatform != null) { + return parentPlatform; + } else { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未查询到此平台"); + } + } + + /** + * 分页查询级联平台 + * + * @param page 当前页 + * @param count 每页条数 + * @return + */ + @GetMapping("/query/{count}/{page}") + @Operation(summary = "分页查询级联平台") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + public PageInfo platforms(@PathVariable int page, @PathVariable int count) { + + PageInfo parentPlatformPageInfo = platformService.queryParentPlatformList(page, count); + if (parentPlatformPageInfo.getList().size() > 0) { + for (ParentPlatform platform : parentPlatformPageInfo.getList()) { + platform.setMobilePositionSubscribe(subscribeHolder.getMobilePositionSubscribe(platform.getServerGBId()) != null); + platform.setCatalogSubscribe(subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) != null); + } + } + return parentPlatformPageInfo; + } + + /** + * 添加上级平台信息 + * + * @param parentPlatform + * @return + */ + @Operation(summary = "添加上级平台信息") + @PostMapping("/add") + @ResponseBody + public void addPlatform(@RequestBody ParentPlatform parentPlatform) { + + if (logger.isDebugEnabled()) { + logger.debug("保存上级平台信息API调用"); + } + if (ObjectUtils.isEmpty(parentPlatform.getName()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) + || ObjectUtils.isEmpty(parentPlatform.getServerIP()) + || ObjectUtils.isEmpty(parentPlatform.getServerPort()) + || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) + || ObjectUtils.isEmpty(parentPlatform.getExpires()) + || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) + || ObjectUtils.isEmpty(parentPlatform.getTransport()) + || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + if (parentPlatform.getServerPort() < 0 || parentPlatform.getServerPort() > 65535) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "error severPort"); + } + + + ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId()); + if (parentPlatformOld != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台 " + parentPlatform.getServerGBId() + " 已存在"); + } + parentPlatform.setCreateTime(DateUtil.getNow()); + parentPlatform.setUpdateTime(DateUtil.getNow()); + boolean updateResult = platformService.add(parentPlatform); + + if (!updateResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败"); + } + } + + /** + * 保存上级平台信息 + * + * @param parentPlatform + * @return + */ + @Operation(summary = "保存上级平台信息") + @PostMapping("/save") + @ResponseBody + public void savePlatform(@RequestBody ParentPlatform parentPlatform) { + + if (logger.isDebugEnabled()) { + logger.debug("保存上级平台信息API调用"); + } + if (ObjectUtils.isEmpty(parentPlatform.getName()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBId()) + || ObjectUtils.isEmpty(parentPlatform.getServerGBDomain()) + || ObjectUtils.isEmpty(parentPlatform.getServerIP()) + || ObjectUtils.isEmpty(parentPlatform.getServerPort()) + || ObjectUtils.isEmpty(parentPlatform.getDeviceGBId()) + || ObjectUtils.isEmpty(parentPlatform.getExpires()) + || ObjectUtils.isEmpty(parentPlatform.getKeepTimeout()) + || ObjectUtils.isEmpty(parentPlatform.getTransport()) + || ObjectUtils.isEmpty(parentPlatform.getCharacterSet()) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + + platformService.update(parentPlatform); + } + + /** + * 登出上级平台 + * + * @param serverGBId 登出上级平台 + * @return + */ + @Operation(summary = "登出上级平台") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") + @PostMapping("/logout/{serverGBId}") + @ResponseBody + public void logoutPlatform(@PathVariable String serverGBId) { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); + platformService.logout(parentPlatform); + } + + + /** + * 删除上级平台 + * + * @param serverGBId 上级平台国标ID + * @return + */ + @Operation(summary = "删除上级平台") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") + @DeleteMapping("/delete/{serverGBId}") + @ResponseBody + public void deletePlatform(@PathVariable String serverGBId) { + + if (logger.isDebugEnabled()) { + logger.debug("删除上级平台API调用"); + } + if (ObjectUtils.isEmpty(serverGBId) + ) { + throw new ControllerException(ErrorCode.ERROR400); + } + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(serverGBId); + if (parentPlatform == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); + } + if (parentPlatformCatch == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); + } + // 发送离线消息,无论是否成功都删除缓存 + try { + commanderForPlatform.unregister(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), (event -> { + // 清空redis缓存 + redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId()); + }), (event -> { + // 清空redis缓存 + redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId()); + redisCatchStorage.delPlatformRegister(parentPlatform.getServerGBId()); + })); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + + boolean deleteResult = storager.deleteParentPlatform(parentPlatform); + storager.delCatalogByPlatformId(parentPlatform.getServerGBId()); + storager.delRelationByPlatformId(parentPlatform.getServerGBId()); + // 停止发送位置订阅定时任务 + String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetting.getServerId() + "_MobilePosition_" + parentPlatform.getServerGBId(); + dynamicTask.stop(key); + // 删除缓存的订阅信息 + subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); + if (!deleteResult) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 查询上级平台是否存在 + * + * @param serverGBId 上级平台国标ID + * @return + */ + @Operation(summary = "查询上级平台是否存在") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") + @GetMapping("/exit/{serverGBId}") + @ResponseBody + public Boolean exitPlatform(@PathVariable String serverGBId) { + + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); + return parentPlatform != null; + } + + /** + * 分页查询级联平台的所有所有通道 + * + * @param page 当前页 + * @param count 每页条数 + * @param platformId 上级平台ID + * @param query 查询内容 + * @param online 是否在线 + * @param channelType 通道类型 + * @return + */ + @Operation(summary = "查询上级平台是否存在") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + @Parameter(name = "platformId", description = "上级平台的国标编号") + @Parameter(name = "catalogId", description = "目录ID") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "channelType", description = "通道类型") + @GetMapping("/channel_list") + @ResponseBody + public PageInfo channelList(int page, int count, + @RequestParam(required = false) String platformId, + @RequestParam(required = false) String catalogId, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Boolean channelType) { + + if (ObjectUtils.isEmpty(platformId)) { + platformId = null; + } + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(platformId) || ObjectUtils.isEmpty(catalogId)) { + catalogId = null; + } + PageInfo channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, platformId, catalogId); + + return channelReduces; + } + + /** + * 向上级平台添加国标通道 + * + * @param param 通道关联参数 + * @return + */ + @Operation(summary = "向上级平台添加国标通道") + @PostMapping("/update_channel_for_gb") + @ResponseBody + public void updateChannelForGB(@RequestBody UpdateChannelParam param) { + + if (logger.isDebugEnabled()) { + logger.debug("给上级平台添加国标通道API调用"); + } + int result = 0; + if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) { + if (param.isAll()) { + logger.info("[国标级联]添加所有通道到上级平台, {}", param.getPlatformId()); + List allChannelForDevice = deviceChannelService.queryAllChannelList(param.getPlatformId()); + result = platformChannelService.updateChannelForGB(param.getPlatformId(), allChannelForDevice, param.getCatalogId()); + } + }else { + result = platformChannelService.updateChannelForGB(param.getPlatformId(), param.getChannelReduces(), param.getCatalogId()); + } + if (result <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 从上级平台移除国标通道 + * + * @param param 通道关联参数 + * @return + */ + @Operation(summary = "从上级平台移除国标通道") + @DeleteMapping("/del_channel_for_gb") + @ResponseBody + public void delChannelForGB(@RequestBody UpdateChannelParam param) { + + if (logger.isDebugEnabled()) { + logger.debug("给上级平台删除国标通道API调用"); + } + int result = 0; + if (param.getChannelReduces() == null || param.getChannelReduces().size() == 0) { + if (param.isAll()) { + logger.info("[国标级联]移除所有通道,上级平台, {}", param.getPlatformId()); + result = platformChannelService.delAllChannelForGB(param.getPlatformId(), param.getCatalogId()); + } + }else { + result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces()); + } + if (result <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 获取目录 + * + * @param platformId 平台ID + * @param parentId 目录父ID + * @return + */ + @Operation(summary = "获取目录") + @Parameter(name = "platformId", description = "上级平台的国标编号", required = true) + @Parameter(name = "parentId", description = "父级目录的国标编号", required = true) + @GetMapping("/catalog") + @ResponseBody + public List getCatalogByPlatform(String platformId, String parentId) { + + if (logger.isDebugEnabled()) { + logger.debug("查询目录,platformId: {}, parentId: {}", platformId, parentId); + } + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台未找到"); + } + if (platformId.equals(parentId)) { + parentId = platform.getDeviceGBId(); + } + + return storager.getChildrenCatalogByPlatform(platformId, parentId); + } + + /** + * 添加目录 + * + * @param platformCatalog 目录 + * @return + */ + @Operation(summary = "添加目录") + @PostMapping("/catalog/add") + @ResponseBody + public void addCatalog(@RequestBody PlatformCatalog platformCatalog) { + + if (logger.isDebugEnabled()) { + logger.debug("添加目录,{}", JSON.toJSONString(platformCatalog)); + } + PlatformCatalog platformCatalogInStore = storager.getCatalog(platformCatalog.getId()); + + if (platformCatalogInStore != null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), platformCatalog.getId() + " already exists"); + } + int addResult = storager.addCatalog(platformCatalog); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + /** + * 编辑目录 + * + * @param platformCatalog 目录 + * @return + */ + @Operation(summary = "编辑目录") + @PostMapping("/catalog/edit") + @ResponseBody + public void editCatalog(@RequestBody PlatformCatalog platformCatalog) { + + if (logger.isDebugEnabled()) { + logger.debug("编辑目录,{}", JSON.toJSONString(platformCatalog)); + } + PlatformCatalog platformCatalogInStore = storager.getCatalog(platformCatalog.getId()); + + if (platformCatalogInStore == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), platformCatalog.getId() + " not exists"); + } + int addResult = storager.updateCatalog(platformCatalog); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); + } + } + + /** + * 删除目录 + * + * @param id 目录Id + * @param platformId 平台Id + * @return + */ + @Operation(summary = "删除目录") + @Parameter(name = "id", description = "目录Id", required = true) + @Parameter(name = "platformId", description = "平台Id", required = true) + @DeleteMapping("/catalog/del") + @ResponseBody + public void delCatalog(String id, String platformId) { + + if (logger.isDebugEnabled()) { + logger.debug("删除目录,{}", id); + } + + if (ObjectUtils.isEmpty(id) || ObjectUtils.isEmpty(platformId)) { + throw new ControllerException(ErrorCode.ERROR400); + } + + int delResult = storager.delCatalog(id); + // 如果删除的是默认目录则根目录设置为默认目录 + PlatformCatalog parentPlatform = storager.queryDefaultCatalogInPlatform(platformId); + + // 默认节点被移除 + if (parentPlatform == null) { + storager.setDefaultCatalog(platformId, platformId); + } + + if (delResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); + } + } + + /** + * 删除关联 + * + * @param platformCatalog 关联的信息 + * @return + */ + @Operation(summary = "删除关联") + @DeleteMapping("/catalog/relation/del") + @ResponseBody + public void delRelation(@RequestBody PlatformCatalog platformCatalog) { + + if (logger.isDebugEnabled()) { + logger.debug("删除关联,{}", JSON.toJSONString(platformCatalog)); + } + int delResult = storager.delRelation(platformCatalog); + + if (delResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); + } + } + + + /** + * 修改默认目录 + * + * @param platformId 平台Id + * @param catalogId 目录Id + * @return + */ + @Operation(summary = "修改默认目录") + @Parameter(name = "catalogId", description = "目录Id", required = true) + @Parameter(name = "platformId", description = "平台Id", required = true) + @PostMapping("/catalog/default/update") + @ResponseBody + public void setDefaultCatalog(String platformId, String catalogId) { + + if (logger.isDebugEnabled()) { + logger.debug("修改默认目录,{},{}", platformId, catalogId); + } + int updateResult = storager.setDefaultCatalog(platformId, catalogId); + + if (updateResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "写入数据库失败"); + } + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/bean/ChannelReduce.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/bean/ChannelReduce.java new file mode 100644 index 0000000..7e49c11 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/bean/ChannelReduce.java @@ -0,0 +1,137 @@ +package com.yfd.monitor.vmanager.gdw2019.platform.bean; + +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 精简的channel信息展示,主要是选择通道的时候展示列表使用 + */ +@Schema(description = "精简的channel信息展示") +public class ChannelReduce { + + /** + * deviceChannel的数据库自增ID + */ + @Schema(description = "deviceChannel的数据库自增ID") + private int id; + + /** + * 通道id + */ + @Schema(description = "通道国标编号") + private String channelId; + + /** + * 设备id + */ + @Schema(description = "设备国标编号") + private String deviceId; + + /** + * 通道名 + */ + @Schema(description = "通道名") + private String name; + + /** + * 生产厂商 + */ + @Schema(description = "生产厂商") + private String manufacturer; + + /** + * wan地址 + */ + @Schema(description = "wan地址") + private String hostAddress; + + /** + * 子节点数 + */ + @Schema(description = "子节点数") + private int subCount; + + /** + * 平台Id + */ + @Schema(description = "平台上级国标编号") + private String platformId; + + /** + * 目录Id + */ + @Schema(description = "目录国标编号") + private String catalogId; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getManufacturer() { + return manufacturer; + } + + public void setManufacturer(String manufacturer) { + this.manufacturer = manufacturer; + } + + public String getHostAddress() { + return hostAddress; + } + + public void setHostAddress(String hostAddress) { + this.hostAddress = hostAddress; + } + + public int getSubCount() { + return subCount; + } + + public void setSubCount(int subCount) { + this.subCount = subCount; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/bean/UpdateChannelParam.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/bean/UpdateChannelParam.java new file mode 100644 index 0000000..7254cfd --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/platform/bean/UpdateChannelParam.java @@ -0,0 +1,57 @@ +package com.yfd.monitor.vmanager.gdw2019.platform.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +/** + * 通道关联参数 +* + */ +@Schema(description = "通道关联参数") +public class UpdateChannelParam { + + @Schema(description = "上级平台的国标编号") + private String platformId; + + @Schema(description = "目录的国标编号") + private String catalogId; + + @Schema(description = "处理所有通道") + private boolean all; + + @Schema(description = "") + private List channelReduces; + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public List getChannelReduces() { + return channelReduces; + } + + public void setChannelReduces(List channelReduces) { + this.channelReduces = channelReduces; + } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/PlayController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/PlayController.java new file mode 100644 index 0000000..59673f4 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/PlayController.java @@ -0,0 +1,475 @@ +package com.yfd.monitor.vmanager.gdw2019.play; + +import cn.hutool.core.util.ObjUtil; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.conf.security.JwtUtils; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.SsrcTransaction; +import com.yfd.monitor.gdw2019.session.VideoStreamSessionManager; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.media.zlm.ZLMRESTfulUtils; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IMediaService; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.*; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.servlet.http.HttpServletRequest; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Tag(name = "国标设备点播") + +@RestController +@RequestMapping("/api/play") +public class PlayController { + + private final static Logger logger = LoggerFactory.getLogger(PlayController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IPlayService playService; + + @Autowired + private IMediaService mediaService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private UserSetting userSetting; + + @Operation(summary = "开始点播") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @GetMapping("/start/{deviceId}/{channelId}") + public DeferredResult> play(HttpServletRequest request, @PathVariable String deviceId, + @PathVariable String channelId) { + + // 获取可用的zlm + Device device = storager.queryVideoDevice(deviceId); + MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + boolean callsubSteam=false; + String[] temp=channelId.split("_"); + DeferredResult> result = + new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + if(temp.length==2){ + channelId=temp[0]; + if("sub".equals(temp[1])){ + callsubSteam = true; + return playService.play_subStream(newMediaServerItem, deviceId, channelId); + } + System.out.println("进入了多屏方法"); + } + // 获取可用的zlm + if (device == null) { + System.out.println("进入了机器人摄像机方法"); + List patrolDeviceList = storager.getRiisPatrolDeviceByCode(deviceId); + if (patrolDeviceList.size() > 0) { + Map map1 = patrolDeviceList.get(0); + Map map = new HashMap(); + String type = map1.get("type").toString(); + if ("1".equals(type) || "2".equals(type) || "3".equals(type) || "4".equals(type)|| "13".equals(type)) { + List riisPatrolDeviceByRobot = storager.getRiisPatrolDeviceByRobot(deviceId); + map.putAll(riisPatrolDeviceByRobot.get(0)); + }else{ + map.putAll(map1); + } + String patroldevice_code = map.get("patroldevice_code").toString(); + // 通过摄像机本身的rtsp流播放视频 + String stream = String.format("%s_%s", patroldevice_code, deviceId); + String app = "rtp"; + String url = map.get("rtspurl").toString(); + JSONObject list = zlmresTfulUtils.getMediaList(newMediaServerItem, app, stream); + if (list == null || ObjUtil.isEmpty(list.getJSONArray("data"))) { + JSONObject streamProxy = zlmresTfulUtils.addStreamProxy(newMediaServerItem, app, stream, url, + false, true, "0"); + System.out.println(streamProxy.toJSONString()); + } + WVPResult result1 = new WVPResult<>(); + result1.setCode(0); + result1.setMsg("成功!"); + String fmp4 = String.format("http://%s:%s/rtp/%s.live.mp4", newMediaServerItem.getIp(), + newMediaServerItem.getHttpPort(), + stream); + String ws_flv = String.format("ws://%s:%s/rtp/%s.live.flv", newMediaServerItem.getIp(), + newMediaServerItem.getHttpPort(), + stream); + + result1.setData(new StreamContent(fmp4, ws_flv)); + result.setResult(result1); + return result; + } + + } + + RequestMessage msg = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + boolean exist = resultHolder.exist(key, null); + msg.setKey(key); + String uuid = UUID.randomUUID().toString(); + msg.setId(uuid); + + DeferredResultEx> deferredResultEx = new DeferredResultEx<>(result); + result.onTimeout(() -> { + logger.info("点播接口等待超时"); + // 释放rtpserver + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播超时"); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误 + deferredResultEx.setFilter(result1 -> { + WVPResult wvpResult1 = (WVPResult)result1; + WVPResult resultStream = new WVPResult<>(); + resultStream.setCode(wvpResult1.getCode()); + resultStream.setMsg(wvpResult1.getMsg()); + if (wvpResult1.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo data = wvpResult1.getData().clone(); + if (userSetting.getUseSourceIpAsStreamIp()) { + data.channgeStreamIp(request.getLocalName()); + } + resultStream.setData(new StreamContent(wvpResult1.getData())); + } + return resultStream; + }); + + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, deferredResultEx); + + if (!exist) { + playService.play(newMediaServerItem, deviceId, channelId, null, null, null); + } + return result; + } + + @Operation(summary = "停止点播") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @GetMapping("/stop/{deviceId}/{channelId}") + public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) { + + logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId)); + + if (deviceId == null || channelId == null) { + throw new ControllerException(ErrorCode.ERROR400); + } + + Device device = storager.queryVideoDevice(deviceId); + MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + String[] temp = channelId.split("_"); + if (temp.length == 2) { + if ("sub".equals(temp[1])) { + String app = "rtp"; + String stream = String.format("%s_%s", deviceId, channelId); + return zlmresTfulUtils.closeStreams(newMediaServerItem, app, stream); + } + System.out.println("进入了多屏方法"); + } + if (device == null) { + System.out.println("进入了机器人摄像机方法"); + List patrolDeviceList = storager.getRiisPatrolDeviceByCode(deviceId); + if (patrolDeviceList.size() > 0) { + Map map1 = patrolDeviceList.get(0); + Map map = new HashMap(); + String type = map1.get("type").toString(); + if ("1".equals(type) || "2".equals(type) || "3".equals(type) || "4".equals(type) || "13".equals(type)) { + List riisPatrolDeviceByRobot = storager.getRiisPatrolDeviceByRobot(deviceId); + map.putAll(riisPatrolDeviceByRobot.get(0)); + } else { + map.putAll(map1); + } + String patroldevice_code = map.get("patroldevice_code").toString(); + // 通过摄像机本身的rtsp流播放视频 + String stream = String.format("%s_%s", patroldevice_code, deviceId); + String app = "rtp"; + JSONObject result = zlmresTfulUtils.closeStreams(newMediaServerItem, app, stream); + if (ObjUtil.isNotEmpty(result.getIntValue("count_closed")) && (result.getIntValue("count_closed") > 0)) { + return result; + } else { + return null; + } + } + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在"); + } + if("Thermal Image".equals(device.getManufacturer())){//大力的红外摄像头(临时处理) + String key=String.format("__defaultVhost__/rtp/%s_%s",deviceId,channelId); + String stream=String.format("%s_%s",deviceId,channelId); + String app="rtp"; + JSONObject result=zlmresTfulUtils.closeStreams(newMediaServerItem,app,stream); + if(ObjUtil.isNotEmpty(result.getIntValue("count_closed"))&&(result.getIntValue("count_closed")>0)){ + return result; + }else{ + return null; + } + } + + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); + if (streamInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); + } + + try { + logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, streamInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + redisCatchStorage.stopPlay(streamInfo); + + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + JSONObject json = new JSONObject(); + json.put("deviceId", deviceId); + json.put("channelId", channelId); + return json; + } + + /** + * 将不是h264的视频通过ffmpeg 转码为h264 + aac + * @param streamId 流ID + */ + @Operation(summary = "将不是h264的视频通过ffmpeg 转码为h264 + aac") + @Parameter(name = "streamId", description = "视频流ID", required = true) + @PostMapping("/convert/{streamId}") + public JSONObject playConvert(@PathVariable String streamId) { + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); + if (streamInfo == null) { + streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + } + if (streamInfo == null) { + logger.warn("视频转码API调用失败!, 视频流已经停止!"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到视频流信息, 视频流可能已经停止"); + } + MediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId()); + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); + if (!rtpInfo.getBoolean("exist")) { + logger.warn("视频转码API调用失败!, 视频流已停止推流!"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到视频流信息, 视频流可能已停止推流"); + } else { + String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), + streamId ); + String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); + JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaInfo, srcUrl, dstUrl, "1000000", true, false, null); + logger.info(jsonObject.toJSONString()); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + JSONObject data = jsonObject.getJSONObject("data"); + if (data != null) { + JSONObject result = new JSONObject(); + result.put("key", data.getString("key")); + StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId, mediaInfo.getId(), false); + result.put("StreamInfo", streamInfoResult); + return result; + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "转码失败"); + } + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "转码失败"); + } + } + } + + /** + * 结束转码 + */ + @Operation(summary = "结束转码") + @Parameter(name = "key", description = "视频流key", required = true) + @Parameter(name = "mediaServerId", description = "流媒体服务ID", required = true) + @PostMapping("/convertStop/{key}") + public void playConvertStop(@PathVariable String key, String mediaServerId) { + if (mediaServerId == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "流媒体:" + mediaServerId + "不存在" ); + } + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "使用的流媒体已经停止运行" ); + }else { + JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaInfo, key); + logger.info(jsonObject.toJSONString()); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + JSONObject data = jsonObject.getJSONObject("data"); + if (data == null || data.getBoolean("flag") == null || !data.getBoolean("flag")) { + throw new ControllerException(ErrorCode.ERROR100 ); + } + }else { + throw new ControllerException(ErrorCode.ERROR100 ); + } + } + } + +// @Operation(summary = "语音广播命令") +// @Parameter(name = "deviceId", description = "设备国标编号", required = true) +// @GetMapping("/broadcast/{deviceId}") +// @PostMapping("/broadcast/{deviceId}") +// public DeferredResult broadcastApi(@PathVariable String deviceId) { +// if (logger.isDebugEnabled()) { +// logger.debug("语音广播API调用"); +// } +// Device device = storager.queryVideoDevice(deviceId); +// DeferredResult result = new DeferredResult<>(3 * 1000L); +// String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId; +// if (resultHolder.exist(key, null)) { +// result.setResult("设备使用中"); +// return result; +// } +// String uuid = UUID.randomUUID().toString(); +// if (device == null) { +// +// resultHolder.put(key, key, result); +// RequestMessage msg = new RequestMessage(); +// msg.setKey(key); +// msg.setId(uuid); +// JSONObject json = new JSONObject(); +// json.put("DeviceID", deviceId); +// json.put("CmdType", "Broadcast"); +// json.put("Result", "Failed"); +// json.put("Description", "Device 不存在"); +// msg.setData(json); +// resultHolder.invokeResult(msg); +// return result; +// } +// try { +// cmder.audioBroadcastCmd(device, (event) -> { +// RequestMessage msg = new RequestMessage(); +// msg.setKey(key); +// msg.setId(uuid); +// JSONObject json = new JSONObject(); +// json.put("DeviceID", deviceId); +// json.put("CmdType", "Broadcast"); +// json.put("Result", "Failed"); +// json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", event.statusCode, event.msg)); +// msg.setData(json); +// resultHolder.invokeResult(msg); +// }); +// } catch (InvalidArgumentException | SipException | ParseException e) { +// logger.error("[命令发送失败] 语音广播: {}", e.getMessage()); +// throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); +// } +// +// result.onTimeout(() -> { +// logger.warn("语音广播操作超时, 设备未返回应答指令"); +// RequestMessage msg = new RequestMessage(); +// msg.setKey(key); +// msg.setId(uuid); +// JSONObject json = new JSONObject(); +// json.put("DeviceID", deviceId); +// json.put("CmdType", "Broadcast"); +// json.put("Result", "Failed"); +// json.put("Error", "Timeout. Device did not response to broadcast command."); +// msg.setData(json); +// resultHolder.invokeResult(msg); +// }); +// resultHolder.put(key, uuid, result); +// return result; +// } + + + @Operation(summary = "语音广播命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "deviceId", description = "通道国标编号", required = true) + @Parameter(name = "timeout", description = "推流超时时间(秒)", required = true) + @GetMapping("/broadcast/{deviceId}/{channelId}") + @PostMapping("/broadcast/{deviceId}/{channelId}") + public AudioBroadcastResult broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout, Boolean broadcastMode) { + if (logger.isDebugEnabled()) { + logger.debug("语音广播API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); + } + if (channelId == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到通道: " + channelId); + } + + return playService.audioBroadcast(device, channelId, broadcastMode); + + } + + + @Operation(summary = "停止语音广播") + @Parameter(name = "deviceId", description = "设备Id", required = true) + @Parameter(name = "channelId", description = "通道Id", required = true) + @GetMapping("/broadcast/stop/{deviceId}/{channelId}") + @PostMapping("/broadcast/stop/{deviceId}/{channelId}") + public void stopBroadcast(@PathVariable String deviceId, @PathVariable String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("停止语音广播API调用"); + } + // try { + // playService.stopAudioBroadcast(deviceId, channelId); + // } catch (InvalidArgumentException | ParseException | SipException e) { + // logger.error("[命令发送失败] 停止语音: {}", e.getMessage()); + // throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + // } + playService.stopAudioBroadcast(deviceId, channelId); + } + + @Operation(summary = "获取所有的ssrc") + @GetMapping("/ssrc") + public JSONObject getSSRC() { + if (logger.isDebugEnabled()) { + logger.debug("获取所有的ssrc"); + } + JSONArray objects = new JSONArray(); + List allSsrc = streamSession.getAllSsrc(); + for (SsrcTransaction transaction : allSsrc) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("deviceId", transaction.getDeviceId()); + jsonObject.put("channelId", transaction.getChannelId()); + jsonObject.put("ssrc", transaction.getSsrc()); + jsonObject.put("streamId", transaction.getStream()); + objects.add(jsonObject); + } + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("data", objects); + jsonObject.put("count", objects.size()); + return jsonObject; + } + +} + diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/bean/AudioBroadcastEvent.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/bean/AudioBroadcastEvent.java new file mode 100644 index 0000000..a52d480 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/bean/AudioBroadcastEvent.java @@ -0,0 +1,9 @@ +package com.yfd.monitor.vmanager.gdw2019.play.bean; + + +/** + * @author lin + */ +public interface AudioBroadcastEvent { + void call(String msg); +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/bean/PlayResult.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/bean/PlayResult.java new file mode 100644 index 0000000..a2849ef --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/play/bean/PlayResult.java @@ -0,0 +1,39 @@ +package com.yfd.monitor.vmanager.gdw2019.play.bean; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.vmanager.bean.WVPResult; +import org.springframework.http.ResponseEntity; +import org.springframework.web.context.request.async.DeferredResult; + +public class PlayResult { + + private DeferredResult> result; + private String uuid; + + private Device device; + + public DeferredResult> getResult() { + return result; + } + + public void setResult(DeferredResult> result) { + this.result = result; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/playback/PlaybackController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/playback/PlaybackController.java new file mode 100644 index 0000000..f5d766f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/playback/PlaybackController.java @@ -0,0 +1,205 @@ +package com.yfd.monitor.vmanager.gdw2019.playback; + +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.exception.ServiceException; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.media.zlm.ZLMRTPServerFactory; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.UUID; + + +@Tag(name = "视频回放") + +@RestController +@RequestMapping("/api/playback") +public class PlaybackController { + + private final static Logger logger = LoggerFactory.getLogger(PlaybackController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IPlayService playService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private UserSetting userSetting; + + @Operation(summary = "开始视频回放") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @GetMapping("/start/{deviceId}/{channelId}") + public DeferredResult> start(@PathVariable String deviceId, @PathVariable String channelId, + String startTime, String endTime) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); + } + + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId; + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + resultHolder.put(key, uuid, result); + + WVPResult wvpResult = new WVPResult<>(); + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); + + playService.playBack(deviceId, channelId, startTime, endTime, null, + playBackResult->{ + wvpResult.setCode(playBackResult.getCode()); + wvpResult.setMsg(playBackResult.getMsg()); + if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo streamInfo = (StreamInfo)playBackResult.getData(); + wvpResult.setData(new StreamContent(streamInfo)); + } + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + + return result; + } + + + @Operation(summary = "停止视频回放") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/stop/{deviceId}/{channelId}/{stream}") + public void playStop( + @PathVariable String deviceId, + @PathVariable String channelId, + @PathVariable String stream) { + if (ObjectUtils.isEmpty(deviceId) || ObjectUtils.isEmpty(channelId) || ObjectUtils.isEmpty(stream)) { + throw new ControllerException(ErrorCode.ERROR400); + } + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + " 未找到"); + } + try { + cmder.streamByeCmd(device, channelId, stream, null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "发送bye失败: " + e.getMessage()); + } + } + + + @Operation(summary = "回放暂停") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @GetMapping("/pause/{streamId}") + public void playPause(@PathVariable String streamId) { + logger.info("playPause: "+streamId); + + try { + playService.pauseRtp(streamId); + } catch (ServiceException e) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), e.getMessage()); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + + @Operation(summary = "回放恢复") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @GetMapping("/resume/{streamId}") + public void playResume(@PathVariable String streamId) { + logger.info("playResume: "+streamId); + try { + playService.resumeRtp(streamId); + } catch (ServiceException e) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), e.getMessage()); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + + @Operation(summary = "回放拖动播放") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @Parameter(name = "seekTime", description = "拖动偏移量,单位s", required = true) + @GetMapping("/seek/{streamId}/{seekTime}") + public void playSeek(@PathVariable String streamId, @PathVariable long seekTime) { + logger.info("playSeek: "+streamId+", "+seekTime); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + logger.warn("streamId不存在!"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); + } + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); + try { + cmder.playSeekCmd(device, streamInfo, seekTime); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + @Operation(summary = "回放倍速播放") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4", required = true) + @GetMapping("/speed/{streamId}/{speed}") + public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) { + logger.info("playSpeed: "+streamId+", "+speed); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + logger.warn("streamId不存在!"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); + } + if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) { + logger.warn("不支持的speed: " + speed); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)"); + } + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); + try { + cmder.playSpeedCmd(device, streamInfo, speed); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/ptz/PtzController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/ptz/PtzController.java new file mode 100644 index 0000000..8ace5dd --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/ptz/PtzController.java @@ -0,0 +1,232 @@ +package com.yfd.monitor.vmanager.gdw2019.ptz; + + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Tag(name = "云台控制") + +@RestController +@RequestMapping("/api/ptz") +public class PtzController { + + private final static Logger logger = LoggerFactory.getLogger(PtzController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private DeferredResultHolder resultHolder; + + /*** + * 云台控制 + * @param deviceId 设备id + * @param channelId 通道id + * @param command 控制指令 + * @param horizonSpeed 水平移动速度 + * @param verticalSpeed 垂直移动速度 + * @param zoomSpeed 缩放速度 + */ + + @Operation(summary = "云台控制") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "command", description = "控制指令,允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop", required = true) + @Parameter(name = "horizonSpeed", description = "水平速度", required = true) + @Parameter(name = "verticalSpeed", description = "垂直速度", required = true) + @Parameter(name = "zoomSpeed", description = "缩放速度", required = true) + @PostMapping("/control/{deviceId}/{channelId}") + public void ptz(@PathVariable String deviceId,@PathVariable String channelId, String command, int horizonSpeed, int verticalSpeed, int zoomSpeed){ + JSONObject jobj=getPanInfo(deviceId,channelId); //统一处理了一下,如果红外摄像头,取了可见光摄像头的设备编号和通道编号 + deviceId=jobj.getStr("deviceId"); + channelId=jobj.getStr("channelId"); + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,command:%s ,horizonSpeed:%d ,verticalSpeed:%d ,zoomSpeed:%d",deviceId, channelId, command, horizonSpeed, verticalSpeed, zoomSpeed)); + } + Device device = storager.queryVideoDevice(deviceId); + int cmdCode = 0; + switch (command){ + case "left": + cmdCode = 2; + break; + case "right": + cmdCode = 1; + break; + case "up": + cmdCode = 8; + break; + case "down": + cmdCode = 4; + break; + case "upleft": + cmdCode = 10; + break; + case "upright": + cmdCode = 9; + break; + case "downleft": + cmdCode = 6; + break; + case "downright": + cmdCode = 5; + break; + case "zoomin": + cmdCode = 16; + break; + case "zoomout": + cmdCode = 32; + break; + case "stop": + break; + default: + break; + } + try { + cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 云台控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + + @Operation(summary = "通用前端控制命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "cmdCode", description = "指令码", required = true) + @Parameter(name = "parameter1", description = "数据一", required = true) + @Parameter(name = "parameter2", description = "数据二", required = true) + @Parameter(name = "combindCode2", description = "组合码二", required = true) + @PostMapping("/front_end_command/{deviceId}/{channelId}") + public void frontEndCommand(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int parameter1, int parameter2, int combindCode2){ + JSONObject jobj=getPanInfo(deviceId,channelId); //统一处理了一下,如果红外摄像头,取了可见光摄像头的设备编号和通道编号 + deviceId=jobj.getStr("deviceId"); + channelId=jobj.getStr("channelId"); + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备云台控制 API调用,deviceId:%s ,channelId:%s ,cmdCode:%d parameter1:%d parameter2:%d",deviceId, channelId, cmdCode, parameter1, parameter2)); + } + Device device = storager.queryVideoDevice(deviceId); + + try { + cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2); + if(cmdCode==130&¶meter1==0&&combindCode2==0){ + storager.updatePatroldeviceTime(deviceId,String.valueOf(parameter2)); + } + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 前端控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "prepos", description = "预置位号", required = true) + @PostMapping("/callDevicePrePos") + public void callDevicePrePos (String deviceId,String channelId,String prepos){ + JSONObject jobj=getPanInfo(deviceId,channelId); //统一处理了一下,如果红外摄像头,取了可见光摄像头的设备编号和通道编号 + deviceId=jobj.getStr("deviceId"); + channelId=jobj.getStr("channelId"); + + Device device = storager.queryVideoDevice(deviceId); + try { + cmder.frontEndCmd(device, channelId, 130, 0, Integer.parseInt(prepos),0); + storager.updatePatroldeviceTime(deviceId,prepos); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 前端控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + @Operation(summary = "预置位查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @GetMapping("/preset/query/{deviceId}/{channelId}") + public DeferredResult presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) { + if (logger.isDebugEnabled()) { + logger.debug("设备预置位查询API调用"); + } + Device device = storager.queryVideoDevice(deviceId); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + DeferredResult result = new DeferredResult (3 * 1000L); + result.onTimeout(()->{ + logger.warn(String.format("获取设备预置位超时")); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("获取设备预置位超时"); + resultHolder.invokeResult(msg); + }); + if (resultHolder.exist(key, null)) { + return result; + } + resultHolder.put(key, uuid, result); + try { + cmder.presetQuery(device, channelId, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + return result; + } + + private JSONObject getPanInfo(String deviceId, String channelId) { + JSONObject jsonObject=new JSONObject(); + jsonObject.putOpt("deviceId",deviceId); + jsonObject.putOpt("channelId",channelId); + List list =storager.getRiisPatrolDeviceByCode(deviceId); + if(list.size()>0){ + Map map=list.get(0); + String channelinfo=map.get("channelinfo").toString(); + if (JSONUtil.isTypeJSONArray(channelinfo)){ + JSONArray jarray= JSONUtil.parseArray(channelinfo); + for (int i = 0; i < jarray.size(); i++) { + JSONObject jobj=jarray.getJSONObject(i); + if(jobj.getStr("channel_type").equals("2")){ //红外 + if(ObjUtil.isNotEmpty(jobj.getStr("pan_code"))){ + jsonObject.putOpt("deviceId",jobj.getStr("pan_code")); + jsonObject.putOpt("channelId",jobj.getStr("pan_channelcode")); + } + } + } + } + } + return jsonObject; + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/record/GBRecordController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/record/GBRecordController.java new file mode 100644 index 0000000..f047d32 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/record/GBRecordController.java @@ -0,0 +1,309 @@ +package com.yfd.monitor.vmanager.gdw2019.record; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.common.VideoManagerConstants; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.bean.RecordInfo; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.DeferredResultEx; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.yfd.monitor.vmanager.bean.WVPResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.Collection; +import java.util.UUID; + +@Tag(name = "国标录像") + +@RestController +@RequestMapping("/api/gb_record") +public class GBRecordController { + + private final static Logger logger = LoggerFactory.getLogger(GBRecordController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IPlayService playService; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private RedisTemplate redisTemplate; + + + @Operation(summary = "硬盘录像机视频录像查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @GetMapping("/queryNvrRecord/{deviceId}/{channelId}") + public DeferredResult> queryNvrRecord(@PathVariable String deviceId, @PathVariable String channelId, String startTime, String endTime) throws InterruptedException { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s", deviceId, startTime, + endTime)); + } + DeferredResult> result = new DeferredResult<>(); + if (!DateUtil.verification(startTime, DateUtil.formatter)) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + if (!DateUtil.verification(endTime, DateUtil.formatter)) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + //根据摄像头id,查询出对应Nvr设备 + Device device = storager.queryNvrDeviceByDeviceId(deviceId, channelId); + // 指定超时时间 1分钟30秒 + String uuid = UUID.randomUUID().toString(); + int sn = (int) ((Math.random() * 9 + 1) * 100000); + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn; + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + try { + // customName 为硬盘录像机对应的摄像机通道 + cmder.recordInfoQuery(device, device.getCustomName(), startTime, endTime, sn, null, null, null, + (eventResult -> { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + })); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, result); + result.onTimeout(() -> { + msg.setData("timeout"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("timeout"); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + return result; + } + + @Operation(summary = "查询摄像机录像时长") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @GetMapping("/queryRecordTimes/{deviceId}/{channelId}") + public String queryRecordTimes(@PathVariable String deviceId, @PathVariable String channelId) throws InterruptedException { + String RecordTimeKey = VideoManagerConstants.REDIS_RECORD_TIMES_PRE +channelId ; + String recordtimes=redisTemplate.opsForValue().get(RecordTimeKey).toString(); + return recordtimes; + } + + + @Operation(summary = "录像查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @GetMapping("/query/{deviceId}/{channelId}") + public DeferredResult> recordinfo(@PathVariable String deviceId, + @PathVariable String channelId, String startTime, + String endTime) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s", deviceId, startTime, + endTime)); + } + DeferredResult> result = new DeferredResult<>(); + if (!DateUtil.verification(startTime, DateUtil.formatter)) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + if (!DateUtil.verification(endTime, DateUtil.formatter)) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + + Device device = storager.queryVideoDevice(deviceId); + // 指定超时时间 1分钟30秒 + String uuid = UUID.randomUUID().toString(); + int sn = (int) ((Math.random() * 9 + 1) * 100000); + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + try { + cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, null, null, null, (eventResult -> { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + })); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, result); + result.onTimeout(()->{ + msg.setData("timeout"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("timeout"); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + return result; + } + @Operation(summary = "硬盘录像机录像下载") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @Parameter(name = "downloadSpeed", description = "下载倍速", required = true) + @GetMapping("/downloadRecord/start/{deviceId}/{channelId}") + public DeferredResult> downloadRecord(@PathVariable String deviceId, @PathVariable String channelId, + String startTime, String endTime, String downloadSpeed) { + + Device nvrdevice = storager.queryNvrDeviceByDeviceId(deviceId, channelId); + deviceId=nvrdevice.getDeviceId();//硬盘录像机 + if (logger.isDebugEnabled()) { + logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed)); + } + + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId; + DeferredResult> result = new DeferredResult<>(30000L); + resultHolder.put(key, uuid, result); + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + + WVPResult wvpResult = new WVPResult<>(); + + playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), null, playBackResult->{ + + wvpResult.setCode(playBackResult.getCode()); + wvpResult.setMsg(playBackResult.getMsg()); + if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo streamInfo = (StreamInfo)playBackResult.getData(); + wvpResult.setData(new StreamContent(streamInfo)); + } + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + + return result; + } + + @Operation(summary = "开始历史媒体下载") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @Parameter(name = "downloadSpeed", description = "下载倍速", required = true) + @GetMapping("/download/start/{deviceId}/{channelId}") + public DeferredResult> download(@PathVariable String deviceId, @PathVariable String channelId, + String startTime, String endTime, String downloadSpeed) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed)); + } + + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId; + DeferredResult> result = new DeferredResult<>(30000L); + resultHolder.put(key, uuid, result); + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + + WVPResult wvpResult = new WVPResult<>(); + + playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), null, playBackResult->{ + + wvpResult.setCode(playBackResult.getCode()); + wvpResult.setMsg(playBackResult.getMsg()); + if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo streamInfo = (StreamInfo)playBackResult.getData(); + wvpResult.setData(new StreamContent(streamInfo)); + } + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + + return result; + } + + @Operation(summary = "停止历史媒体下载") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/download/stop/{deviceId}/{channelId}/{stream}") + public void playStop(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId)); + } + + if (deviceId == null || channelId == null) { + throw new ControllerException(ErrorCode.ERROR400); + } + + Device device = deviceService.getDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "未找到"); + } + + try { + cmder.streamByeCmd(device, channelId, stream, null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + logger.error("[停止历史媒体下载]停止历史媒体下载,发送BYE失败 {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + @Operation(summary = "获取历史媒体下载进度") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/download/progress/{deviceId}/{channelId}/{stream}") + public StreamContent getProgress(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable String stream) { + StreamInfo downLoadInfo = playService.getDownLoadInfo(deviceId, channelId, stream); + if (downLoadInfo == null) { + throw new ControllerException(ErrorCode.ERROR404); + } + return new StreamContent(downLoadInfo); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/stationnode/StationNodeController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/stationnode/StationNodeController.java new file mode 100644 index 0000000..3807e36 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/gdw2019/stationnode/StationNodeController.java @@ -0,0 +1,62 @@ +package com.yfd.monitor.vmanager.gdw2019.stationnode; + + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.SubstationNode; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommanderFroNode; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +@Tag(name = "边缘节点控制") + +@RestController +@RequestMapping("/api/stationnode") +public class StationNodeController { + + private final static Logger logger = LoggerFactory.getLogger(StationNodeController.class); + + @Autowired + private SIPCommanderFroNode cmder; + + @Resource + private SubstationNode substationNode; + + /*** + * 向边缘节点下发巡视任务 + * @param stationcode 变电站编号 + * @param taskid 巡视任务ID + */ + @Operation(summary = "下发巡视任务") + @Parameter(name = "stationcode", description = "变电站编号", required = true) + @Parameter(name = "taskid", description = "巡视任务ID", required = true) + @PostMapping("/sendTaskToNode") + public void sendTaskToNode(String stationcode,String taskid){ + substationNode.setStationCode("0001"); + substationNode.setStationGBId("34020000002010000001"); + substationNode.setStationIp("192.168.1.13"); + substationNode.setStationGBPort("5063"); + substationNode.setTransport("TCP"); + try { + cmder.sendTaskToNode(substationNode); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 云台控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/log/LogController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/log/LogController.java new file mode 100644 index 0000000..63ecbe7 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/log/LogController.java @@ -0,0 +1,93 @@ +package com.yfd.monitor.vmanager.log; + +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.service.ILogService; +import com.yfd.monitor.storager.dao.dto.LogDto; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "日志管理") + +@RestController +@RequestMapping("/api/log") +public class LogController { + + private final static Logger logger = LoggerFactory.getLogger(LogController.class); + + @Autowired + private ILogService logService; + + @Autowired + private UserSetting userSetting; + + /** + * 分页查询日志 + * + * @param query 查询内容 + * @param page 当前页 + * @param count 每页查询数量 + * @param type 类型 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + @GetMapping("/all") + @Operation(summary = "分页查询报警") + @Parameter(name = "query", description = "查询内容", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "type", description = "类型", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + public PageInfo getAll( + @RequestParam int page, + @RequestParam int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) String type, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime + ) { + if (ObjectUtils.isEmpty(query)) { + query = null; + } + + if (!userSetting.getLogInDatebase()) { + logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); + } + + if (ObjectUtils.isEmpty(startTime)) { + startTime = null; + }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN); + } + + if (ObjectUtils.isEmpty(endTime)) { + endTime = null; + }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){ + throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN); + } + + return logService.getAll(page, count, query, type, startTime, endTime); + } + + /** + * 清空日志 + * + */ + @Operation(summary = "停止视频回放") + @DeleteMapping("/clear") + public void clear() { + logService.clear(); + } + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/server/ServerController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/server/ServerController.java new file mode 100644 index 0000000..e8163c5 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/server/ServerController.java @@ -0,0 +1,269 @@ +package com.yfd.monitor.vmanager.server; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.SystemAllInfo; +import com.yfd.monitor.common.VersionPo; +import com.yfd.monitor.conf.SipConfig; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.VersionInfo; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.ParentPlatform; +import com.yfd.monitor.media.zlm.ZlmHttpHookSubscribe; +import com.yfd.monitor.media.zlm.dto.IHookSubscribe; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.*; +import com.yfd.monitor.service.bean.MediaServerLoad; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.ResourceBaceInfo; +import com.yfd.monitor.vmanager.bean.ResourceInfo; +import com.yfd.monitor.vmanager.bean.SystemConfigInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@SuppressWarnings("rawtypes") +@Tag(name = "服务控制") + +@RestController +@RequestMapping("/api/server") +public class ServerController { + + @Autowired + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private VersionInfo versionInfo; + + @Autowired + private SipConfig sipConfig; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService channelService; + + @Autowired + private IStreamPushService pushService; + + + @Autowired + private IStreamProxyService proxyService; + + + @Value("${server.port}") + private int serverPort; + + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + + @GetMapping(value = "/media_server/list") + @ResponseBody + @Operation(summary = "流媒体服务列表") + public List getMediaServerList() { + return mediaServerService.getAll(); + } + + @GetMapping(value = "/media_server/online/list") + @ResponseBody + @Operation(summary = "在线流媒体服务列表") + public List getOnlineMediaServerList() { + return mediaServerService.getAllOnline(); + } + + @GetMapping(value = "/media_server/one/{id}") + @ResponseBody + @Operation(summary = "停止视频回放") + @Parameter(name = "id", description = "流媒体服务ID", required = true) + public MediaServerItem getMediaServer(@PathVariable String id) { + return mediaServerService.getOne(id); + } + + @Operation(summary = "测试流媒体服务") + @Parameter(name = "ip", description = "流媒体服务IP", required = true) + @Parameter(name = "port", description = "流媒体服务HTT端口", required = true) + @Parameter(name = "secret", description = "流媒体服务secret", required = true) + @GetMapping(value = "/media_server/check") + @ResponseBody + public MediaServerItem checkMediaServer(@RequestParam String ip, @RequestParam int port, @RequestParam String secret) { + return mediaServerService.checkMediaServer(ip, port, secret); + } + + @Operation(summary = "测试流媒体录像管理服务") + @Parameter(name = "ip", description = "流媒体服务IP", required = true) + @Parameter(name = "port", description = "流媒体服务HTT端口", required = true) + @GetMapping(value = "/media_server/record/check") + @ResponseBody + public void checkMediaRecordServer(@RequestParam String ip, @RequestParam int port) { + boolean checkResult = mediaServerService.checkMediaRecordServer(ip, port); + if (!checkResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败"); + } + } + + @Operation(summary = "保存流媒体服务") + @Parameter(name = "mediaServerItem", description = "流媒体信息", required = true) + @PostMapping(value = "/media_server/save") + @ResponseBody + public void saveMediaServer(@RequestBody MediaServerItem mediaServerItem) { + MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId()); + + if (mediaServerItemInDatabase != null) { + mediaServerService.update(mediaServerItem); + } else { + mediaServerService.add(mediaServerItem); + } + } + + @Operation(summary = "移除流媒体服务") + @Parameter(name = "id", description = "流媒体ID", required = true) + @DeleteMapping(value = "/media_server/delete") + @ResponseBody + public void deleteMediaServer(@RequestParam String id) { + if (mediaServerService.getOne(id) == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到此节点"); + } + mediaServerService.delete(id); + mediaServerService.deleteDb(id); + } + + + @Operation(summary = "重启服务") + @GetMapping(value = "/restart") + @ResponseBody + public void restart() { +// taskExecutor.execute(()-> { +// try { +// Thread.sleep(3000); +// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider"); +// SipStackImpl stack = (SipStackImpl) up.getSipStack(); +// stack.stop(); +// Iterator listener = stack.getListeningPoints(); +// while (listener.hasNext()) { +// stack.deleteListeningPoint((ListeningPoint) listener.next()); +// } +// Iterator providers = stack.getSipProviders(); +// while (providers.hasNext()) { +// stack.deleteSipProvider((SipProvider) providers.next()); +// } +// VManageBootstrap.restart(); +// } catch (InterruptedException | ObjectInUseException e) { +// throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); +// } +// }); + } + + @Operation(summary = "获取系统信息信息") + @GetMapping(value = "/system/configInfo") + @ResponseBody + public SystemConfigInfo getConfigInfo() { + SystemConfigInfo systemConfigInfo = new SystemConfigInfo(); + systemConfigInfo.setVersion(versionInfo.getVersion()); + systemConfigInfo.setSip(sipConfig); + systemConfigInfo.setAddOn(userSetting); + systemConfigInfo.setServerPort(serverPort); + return systemConfigInfo; + } + + @Operation(summary = "获取版本信息") + @GetMapping(value = "/version") + @ResponseBody + public VersionPo VersionPogetVersion() { + return versionInfo.getVersion(); + } + + @GetMapping(value = "/config") + @Operation(summary = "获取配置信息") + @Parameter(name = "type", description = "配置类型(sip, base)", required = true) + @ResponseBody + public JSONObject getVersion(String type) { + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("server.port", serverPort); + if (ObjectUtils.isEmpty(type)) { + jsonObject.put("sip", JSON.toJSON(sipConfig)); + jsonObject.put("base", JSON.toJSON(userSetting)); + } else { + switch (type) { + case "sip": + jsonObject.put("sip", sipConfig); + break; + case "base": + jsonObject.put("base", userSetting); + break; + default: + break; + } + } + return jsonObject; + } + + @GetMapping(value = "/hooks") + @ResponseBody + @Operation(summary = "获取当前所有hook") + public List getHooks() { + return zlmHttpHookSubscribe.getAll(); + } + + @GetMapping(value = "/system/info") + @ResponseBody + @Operation(summary = "获取系统信息") + public SystemAllInfo getSystemInfo() { + SystemAllInfo systemAllInfo = redisCatchStorage.getSystemInfo(); + + return systemAllInfo; + } + + @GetMapping(value = "/media_server/load") + @ResponseBody + @Operation(summary = "获取负载信息") + public List getMediaLoad() { + List result = new ArrayList<>(); + List allOnline = mediaServerService.getAllOnline(); + if (allOnline.size() == 0) { + return result; + }else { + for (MediaServerItem mediaServerItem : allOnline) { + result.add(mediaServerService.getLoad(mediaServerItem)); + } + } + return result; + } + + @GetMapping(value = "/resource/info") + @ResponseBody + @Operation(summary = "获取负载信息") + public ResourceInfo getResourceInfo() { + ResourceInfo result = new ResourceInfo(); + ResourceBaceInfo deviceInfo = deviceService.getOverview(); + result.setDevice(deviceInfo); + ResourceBaceInfo channelInfo = channelService.getOverview(); + result.setChannel(channelInfo); + ResourceBaceInfo pushInfo = pushService.getOverview(); + result.setPush(pushInfo); + ResourceBaceInfo proxyInfo = proxyService.getOverview(); + result.setProxy(proxyInfo); + + return result; + } + + +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/streamProxy/StreamProxyController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/streamProxy/StreamProxyController.java new file mode 100644 index 0000000..c36a41c --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/streamProxy/StreamProxyController.java @@ -0,0 +1,129 @@ +package com.yfd.monitor.vmanager.streamProxy; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.media.zlm.dto.StreamProxyItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IStreamProxyService; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.StreamContent; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +@SuppressWarnings("rawtypes") +/** + * 拉流代理接口 + */ +@Tag(name = "拉流代理", description = "") +@Controller + +@RequestMapping(value = "/api/proxy") +public class StreamProxyController { + + private final static Logger logger = LoggerFactory.getLogger(StreamProxyController.class); + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IStreamProxyService streamProxyService; + + + @Operation(summary = "分页查询流代理") + @Parameter(name = "page", description = "当前页") + @Parameter(name = "count", description = "每页查询数量") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @GetMapping(value = "/list") + @ResponseBody + public PageInfo list(@RequestParam(required = false)Integer page, + @RequestParam(required = false)Integer count, + @RequestParam(required = false)String query, + @RequestParam(required = false)Boolean online ){ + + return streamProxyService.getAll(page, count); + } + + @Operation(summary = "保存代理", parameters = { + @Parameter(name = "param", description = "代理参数", required = true), + }) + @PostMapping(value = "/save") + @ResponseBody + public StreamContent save(@RequestBody StreamProxyItem param){ + logger.info("添加代理: " + JSONObject.toJSONString(param)); + if (ObjectUtils.isEmpty(param.getMediaServerId())) { + param.setMediaServerId("auto"); + } + if (ObjectUtils.isEmpty(param.getType())) { + param.setType("default"); + } + if (ObjectUtils.isEmpty(param.getGbId())) { + param.setGbId(null); + } + return new StreamContent(streamProxyService.save(param)); + } + + @GetMapping(value = "/ffmpeg_cmd/list") + @ResponseBody + @Operation(summary = "获取ffmpeg.cmd模板") + @Parameter(name = "mediaServerId", description = "流媒体ID", required = true) + public JSONObject getFFmpegCMDs(@RequestParam String mediaServerId){ + logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId ); + + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体: " + mediaServerId + "未找到"); + } + return streamProxyService.getFFmpegCMDs(mediaServerItem); + } + + @DeleteMapping(value = "/del") + @ResponseBody + @Operation(summary = "移除代理") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void del(@RequestParam String app, @RequestParam String stream){ + logger.info("移除代理: " + app + "/" + stream); + if (app == null || stream == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), app == null ?"app不能为null":"stream不能为null"); + }else { + streamProxyService.del(app, stream); + } + } + + @GetMapping(value = "/start") + @ResponseBody + @Operation(summary = "启用代理") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void start(String app, String stream){ + logger.info("启用代理: " + app + "/" + stream); + boolean result = streamProxyService.start(app, stream); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @GetMapping(value = "/stop") + @ResponseBody + @Operation(summary = "停用代理") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void stop(String app, String stream){ + logger.info("停用代理: " + app + "/" + stream); + boolean result = streamProxyService.stop(app, stream); + if (!result) { + logger.info("停用代理失败: " + app + "/" + stream); + throw new ControllerException(ErrorCode.ERROR100); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/streamPush/StreamPushController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/streamPush/StreamPushController.java new file mode 100644 index 0000000..e973c28 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/streamPush/StreamPushController.java @@ -0,0 +1,280 @@ +package com.yfd.monitor.vmanager.streamPush; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.security.SecurityUtils; +import com.yfd.monitor.conf.security.dto.LoginUser; +import com.yfd.monitor.gdw2019.bean.GbStream; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.media.zlm.dto.StreamPushItem; +import com.yfd.monitor.service.IMediaServerService; +import com.yfd.monitor.service.IMediaService; +import com.yfd.monitor.service.IStreamPushService; +import com.yfd.monitor.service.impl.StreamPushUploadFileHandler; +import com.yfd.monitor.vmanager.bean.*; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Tag(name = "推流信息管理") +@Controller + +@RequestMapping(value = "/api/push") +public class StreamPushController { + + private final static Logger logger = LoggerFactory.getLogger(StreamPushController.class); + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IMediaService mediaService; + + @Autowired + private UserSetting userSetting; + + @GetMapping(value = "/list") + @ResponseBody + @Operation(summary = "推流列表查询") + @Parameter(name = "page", description = "当前页") + @Parameter(name = "count", description = "每页查询数量") + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "pushing", description = "是否正在推流") + @Parameter(name = "mediaServerId", description = "流媒体ID") + public PageInfo list(@RequestParam(required = false)Integer page, + @RequestParam(required = false)Integer count, + @RequestParam(required = false)String query, + @RequestParam(required = false)Boolean pushing, + @RequestParam(required = false)String mediaServerId ){ + + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } + PageInfo pushList = streamPushService.getPushList(page, count, query, pushing, mediaServerId); + return pushList; + } + + @PostMapping(value = "/save_to_gb") + @ResponseBody + @Operation(summary = "将推流添加到国标") + public void saveToGB(@RequestBody GbStream stream){ + if (!streamPushService.saveToGB(stream)){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + + @DeleteMapping(value = "/remove_form_gb") + @ResponseBody + @Operation(summary = "将推流移出到国标") + public void removeFormGB(@RequestBody GbStream stream){ + if (!streamPushService.removeFromGB(stream)){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + + @PostMapping(value = "/stop") + @ResponseBody + @Operation(summary = "中止一个推流") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void stop(String app, String streamId){ + if (!streamPushService.stop(app, streamId)){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @DeleteMapping(value = "/batchStop") + @ResponseBody + @Operation(summary = "中止多个推流") + public void batchStop(@RequestBody BatchGBStreamParam batchGBStreamParam){ + if (batchGBStreamParam.getGbStreams().size() == 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + if (!streamPushService.batchStop(batchGBStreamParam.getGbStreams())){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @PostMapping(value = "upload") + @ResponseBody + public DeferredResult>> uploadChannelFile(@RequestParam(value = "file") MultipartFile file){ + + // 最多处理文件一个小时 + DeferredResult>> result = new DeferredResult<>(60*60*1000L); + // 录像查询以channelId作为deviceId查询 + String key = DeferredResultHolder.UPLOAD_FILE_CHANNEL; + String uuid = UUID.randomUUID().toString(); + logger.info("通道导入文件类型: {}",file.getContentType() ); + if (file.isEmpty()) { + logger.warn("通道导入文件为空"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("文件为空"); + result.setResult(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(wvpResult)); + return result; + } + if (file.getContentType() == null) { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("无法识别文件类型"); + result.setResult(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(wvpResult)); + return result; + } + // 同时只处理一个文件 + if (resultHolder.exist(key, null)) { + logger.warn("已有导入任务正在执行"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("已有导入任务正在执行"); + result.setResult(ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(wvpResult)); + return result; + } + + resultHolder.put(key, uuid, result); + result.onTimeout(()->{ + logger.warn("通道导入超时,可能文件过大"); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("导入超时,可能文件过大"); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + }); + //获取文件流 + InputStream inputStream = null; + try { + String name = file.getName(); + inputStream = file.getInputStream(); + } catch (IOException e) { + logger.error("未处理的异常 ", e); + } + try { + //传入参数 + ExcelReader excelReader = EasyExcel.read(inputStream, StreamPushExcelDto.class, + new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId(), (errorStreams, errorGBs)->{ + logger.info("通道导入成功,存在重复App+Stream为{}个,存在国标ID为{}个", errorStreams.size(), errorGBs.size()); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult>> wvpResult = new WVPResult<>(); + if (errorStreams.size() == 0 && errorGBs.size() == 0) { + wvpResult.setCode(0); + wvpResult.setMsg("成功"); + }else { + wvpResult.setCode(1); + wvpResult.setMsg("导入成功。但是存在重复数据"); + Map> errorData = new HashMap<>(); + errorData.put("gbId", errorGBs); + errorData.put("stream", errorStreams); + wvpResult.setData(errorData); + } + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + })).build(); + ReadSheet readSheet = EasyExcel.readSheet(0).build(); + excelReader.read(readSheet); + excelReader.finish(); + }catch (Exception e) { + logger.warn("通道导入失败:", e); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("通道导入失败: " + e.getMessage() ); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + } + + + return result; + } + + /** + * 获取推流播放地址 + * @param app 应用名 + * @param stream 流id + * @return + */ + @GetMapping(value = "/getPlayUrl") + @ResponseBody + @Operation(summary = "获取推流播放地址") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + @Parameter(name = "mediaServerId", description = "媒体服务器id") + public StreamContent getPlayUrl(@RequestParam String app, @RequestParam String stream, + @RequestParam(required = false) String mediaServerId){ + boolean authority = false; + // 是否登陆用户, 登陆用户返回完整信息 + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo!= null) { + authority = true; + } + StreamPushItem push = streamPushService.getPush(app, stream); + if (push != null && !push.isSelf()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "来自其他平台的推流信息"); + } + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + if (streamInfo == null){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取播放地址失败"); + } + return new StreamContent(streamInfo); + } + + /** + * 添加推流信息 + * @param stream 推流信息 + * @return + */ + @PostMapping(value = "/add") + @ResponseBody + @Operation(summary = "添加推流信息") + public void add(@RequestBody StreamPushItem stream){ + if (ObjectUtils.isEmpty(stream.getGbId())) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "国标ID不可为空"); + } + if (ObjectUtils.isEmpty(stream.getApp()) && ObjectUtils.isEmpty(stream.getStream())) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "app或stream不可为空"); + } + stream.setStatus(false); + stream.setPushIng(false); + stream.setAliveSecond(0L); + stream.setTotalReaderCount("0"); + if (!streamPushService.add(stream)) { + throw new ControllerException(ErrorCode.ERROR100); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/user/RoleController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/user/RoleController.java new file mode 100644 index 0000000..14ae939 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/user/RoleController.java @@ -0,0 +1,75 @@ +package com.yfd.monitor.vmanager.user; + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.security.SecurityUtils; +import com.yfd.monitor.service.IRoleService; +import com.yfd.monitor.storager.dao.dto.Role; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "角色管理") + +@RestController +@RequestMapping("/api/role") +public class RoleController { + + @Autowired + private IRoleService roleService; + + @PostMapping("/add") + @Operation(summary = "添加角色") + @Parameter(name = "name", description = "角色名", required = true) + @Parameter(name = "authority", description = "权限(自行定义内容,目前未使用)", required = true) + public void add(@RequestParam String name, + @RequestParam(required = false) String authority){ + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + if (currenRoleId != 1) { + // 只用角色id为1才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR403); + } + + Role role = new Role(); + role.setName(name); + role.setAuthority(authority); + role.setCreateTime(DateUtil.getNow()); + role.setUpdateTime(DateUtil.getNow()); + + int addResult = roleService.add(role); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @DeleteMapping("/delete") + @Operation(summary = "删除角色") + @Parameter(name = "id", description = "用户Id", required = true) + public void delete(@RequestParam Integer id){ + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + if (currenRoleId != 1) { + // 只用角色id为0才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR403); + } + int deleteResult = roleService.delete(id); + + if (deleteResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @GetMapping("/all") + @Operation(summary = "查询角色") + public List all(){ + // 获取当前登录用户id + List allRoles = roleService.getAll(); + return roleService.getAll(); + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/vmanager/user/UserController.java b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/user/UserController.java new file mode 100644 index 0000000..fbacdbb --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/vmanager/user/UserController.java @@ -0,0 +1,227 @@ +package com.yfd.monitor.vmanager.user; + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.conf.security.JwtUtils; +import com.yfd.monitor.conf.security.SecurityUtils; +import com.yfd.monitor.conf.security.dto.LoginUser; +import com.yfd.monitor.service.IRoleService; +import com.yfd.monitor.service.IUserService; +import com.yfd.monitor.storager.dao.dto.Role; +import com.yfd.monitor.storager.dao.dto.User; +import com.yfd.monitor.utils.DateUtil; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.vmanager.bean.WVPResult; +import com.github.pagehelper.PageInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.util.DigestUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import javax.security.sasl.AuthenticationException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.LocalDateTime; +import java.util.List; + +@Tag(name = "用户管理") +@RestController +@RequestMapping("/api/user") +public class UserController { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private IUserService userService; + + @Autowired + private IRoleService roleService; + + @GetMapping("/login") + @PostMapping("/login") + @Operation(summary = "登录", description = "登录成功后返回AccessToken, 可以从返回值获取到也可以从响应头中获取到," + + "后续的请求需要添加请求头 'access-token'或者放在参数里") + + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "password", description = "密码(32位md5加密)", required = true) + public LoginUser login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password){ + LoginUser user; + try { + user = SecurityUtils.login(username, password, authenticationManager); + } catch (AuthenticationException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + if (user == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误"); + }else { + String jwt = JwtUtils.createToken(username, password); + response.setHeader(JwtUtils.getHeader(), jwt); + user.setAccessToken(jwt); + } + return user; + } + + + @PostMapping("/changePassword") + @Operation(summary = "修改密码") + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "oldpassword", description = "旧密码(已md5加密的密码)", required = true) + @Parameter(name = "password", description = "新密码(未md5加密的密码)", required = true) + public void changePassword(@RequestParam String oldPassword, @RequestParam String password){ + // 获取当前登录用户id + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo== null) { + throw new ControllerException(ErrorCode.ERROR100); + } + String username = userInfo.getUsername(); + LoginUser user = null; + try { + user = SecurityUtils.login(username, oldPassword, authenticationManager); + if (user == null) { + throw new ControllerException(ErrorCode.ERROR100); + } + //int userId = SecurityUtils.getUserId(); + boolean result = userService.changePassword(user.getId(), DigestUtils.md5DigestAsHex(password.getBytes())); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } catch (AuthenticationException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } + } + + + @PostMapping("/add") + @Operation(summary = "添加用户") + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "password", description = "密码(未md5加密的密码)", required = true) + @Parameter(name = "roleId", description = "角色ID", required = true) + public void add(@RequestParam String username, + @RequestParam String password, + @RequestParam Integer roleId){ + if (ObjectUtils.isEmpty(username) || ObjectUtils.isEmpty(password) || roleId == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "参数不可为空"); + } + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + if (currenRoleId != 1) { + // 只用角色id为1才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); + } + User user = new User(); + user.setUsername(username); + user.setPassword(DigestUtils.md5DigestAsHex(password.getBytes())); + //新增用户的pushKey的生成规则为md5(时间戳+用户名) + user.setPushKey(DigestUtils.md5DigestAsHex((System.currentTimeMillis()+password).getBytes())); + Role role = roleService.getRoleById(roleId); + + if (role == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "角色不存在"); + } + user.setRole(role); + user.setCreateTime(DateUtil.getNow()); + user.setUpdateTime(DateUtil.getNow()); + int addResult = userService.addUser(user); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @DeleteMapping("/delete") + @Operation(summary = "删除用户") + @Parameter(name = "id", description = "用户Id", required = true) + public void delete(@RequestParam Integer id){ + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + if (currenRoleId != 1) { + // 只用角色id为0才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); + } + int deleteResult = userService.deleteUser(id); + if (deleteResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @GetMapping("/all") + @Operation(summary = "查询用户") + public List all(){ + // 获取当前登录用户id + return userService.getAllUsers(); + } + + /** + * 分页查询用户 + * + * @param page 当前页 + * @param count 每页查询数量 + * @return 分页用户列表 + */ + @GetMapping("/users") + @Operation(summary = "分页查询用户") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + public PageInfo users(int page, int count) { + return userService.getUsers(page, count); + } + + @RequestMapping("/changePushKey") + @Operation(summary = "修改pushkey") + @Parameter(name = "userId", description = "用户Id", required = true) + @Parameter(name = "pushKey", description = "新的pushKey", required = true) + public void changePushKey(@RequestParam Integer userId,@RequestParam String pushKey) { + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + WVPResult result = new WVPResult<>(); + if (currenRoleId != 1) { + // 只用角色id为0才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); + } + int resetPushKeyResult = userService.changePushKey(userId,pushKey); + if (resetPushKeyResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @PostMapping("/changePasswordForAdmin") + @Operation(summary = "管理员修改普通用户密码") + @Parameter(name = "adminId", description = "管理员id", required = true) + @Parameter(name = "userId", description = "用户id", required = true) + @Parameter(name = "password", description = "新密码(未md5加密的密码)", required = true) + public void changePasswordForAdmin(@RequestParam int userId, @RequestParam String password) { + // 获取当前登录用户id + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo == null) { + throw new ControllerException(ErrorCode.ERROR100); + } + Role role = userInfo.getRole(); + if (role != null && role.getId() == 1) { + boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes())); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + } + + @PostMapping("/userInfo") + @Operation(summary = "管理员修改普通用户密码") + public LoginUser getUserInfo() { + // 获取当前登录用户id + // LoginUser userInfo = SecurityUtils.getUserInfo(); + // if (userInfo == null) { + // throw new ControllerException(ErrorCode.ERROR100); + // } + List allUsers = userService.getAllUsers(); + if (allUsers.size() > 0) { + User user = allUsers.get(0); + return new LoginUser(user, LocalDateTime.now()); + } else { + throw new ControllerException(ErrorCode.ERROR100); + } + + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiControlController.java b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiControlController.java new file mode 100644 index 0000000..0664a87 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiControlController.java @@ -0,0 +1,155 @@ +package com.yfd.monitor.web.gdw2019; + +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * API兼容:设备控制 + */ + +@RestController +@RequestMapping(value = "/api/v1/control") +public class ApiControlController { + + private final static Logger logger = LoggerFactory.getLogger(ApiControlController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + /** + * 设备控制 - 云台控制 + * @param serial 设备编号 + * @param command 控制指令 允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop + * @param channel 通道序号 + * @param code 通道编号 + * @param speed 速度(0~255) 默认值: 129 + * @return + */ + @RequestMapping(value = "/ptz") + private void list(String serial,String command, + @RequestParam(required = false)Integer channel, + @RequestParam(required = false)String code, + @RequestParam(required = false)Integer speed){ + + if (logger.isDebugEnabled()) { + logger.debug("模拟接口> 设备云台控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,speed:{} ", + serial, code, command, speed); + } + if (channel == null) {channel = 0;} + if (speed == null) {speed = 0;} + Device device = storager.queryVideoDevice(serial); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); + } + int cmdCode = 0; + switch (command){ + case "left": + cmdCode = 2; + break; + case "right": + cmdCode = 1; + break; + case "up": + cmdCode = 8; + break; + case "down": + cmdCode = 4; + break; + case "upleft": + cmdCode = 10; + break; + case "upright": + cmdCode = 9; + break; + case "downleft": + cmdCode = 6; + break; + case "downright": + cmdCode = 5; + break; + case "zoomin": + cmdCode = 16; + break; + case "zoomout": + cmdCode = 32; + break; + case "stop": + cmdCode = 0; + break; + default: + break; + } + // 默认值 50 + try { + cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 云台控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + /** + * 设备控制 - 预置位控制 + * @param serial 设备编号 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param channel 通道序号, 默认值: 1 + * @param command 控制指令 允许值: set, goto, remove + * @param preset 预置位编号(1~255) + * @param name 预置位名称, command=set 时有效 + * @return + */ + @RequestMapping(value = "/preset") + private void list(String serial,String command, + @RequestParam(required = false)Integer channel, + @RequestParam(required = false)String code, + @RequestParam(required = false)String name, + @RequestParam(required = false)Integer preset){ + + if (logger.isDebugEnabled()) { + logger.debug("模拟接口> 预置位控制 API调用,deviceId:{} ,channelId:{} ,command:{} ,name:{} ,preset:{} ", + serial, code, command, name, preset); + } + + if (channel == null) {channel = 0;} + Device device = storager.queryVideoDevice(serial); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); + } + int cmdCode = 0; + switch (command){ + case "set": + cmdCode = 129; + break; + case "goto": + cmdCode = 130; + break; + case "remove": + cmdCode = 131; + break; + default: + break; + } + try { + cmder.frontEndCmd(device, code, cmdCode, 0, preset, 0); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 预置位控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiController.java b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiController.java new file mode 100644 index 0000000..e45bb8b --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiController.java @@ -0,0 +1,106 @@ +package com.yfd.monitor.web.gdw2019; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.SipConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * API兼容:系统接口 + */ +@Controller + +@RequestMapping(value = "/api/v1") +public class ApiController { + + private final static Logger logger = LoggerFactory.getLogger(ApiController.class); + + @Autowired + private SipConfig sipConfig; + + + @RequestMapping("/getserverinfo") + private JSONObject getserverinfo(){ + JSONObject result = new JSONObject(); + result.put("Authorization","ceshi"); + result.put("Hardware",""); + result.put("InterfaceVersion","2.5.5"); + result.put("IsDemo",""); + result.put("Hardware","false"); + result.put("APIAuth","false"); + result.put("RemainDays","永久"); + result.put("RunningTime",""); + result.put("ServerTime","2020-09-02 17:11"); + result.put("StartUpTime","2020-09-02 17:11"); + result.put("Server",""); + result.put("SIPSerial", sipConfig.getId()); + result.put("SIPRealm", sipConfig.getDomain()); + result.put("SIPHost", sipConfig.getIp()); + result.put("SIPPort", sipConfig.getPort()); + result.put("ChannelCount","1000"); + result.put("VersionType",""); + result.put("LogoMiniText",""); + result.put("LogoText",""); + result.put("CopyrightText",""); + + return result; + } + + @RequestMapping(value = "/userinfo") + private JSONObject userinfo(){ +// JSONObject result = new JSONObject(); +// result.put("ID","ceshi"); +// result.put("Hardware",""); +// result.put("InterfaceVersion","2.5.5"); +// result.put("IsDemo",""); +// result.put("Hardware","false"); +// result.put("APIAuth","false"); +// result.put("RemainDays","永久"); +// result.put("RunningTime",""); +// result.put("ServerTime","2020-09-02 17:11"); +// result.put("StartUpTime","2020-09-02 17:11"); +// result.put("Server",""); +// result.put("SIPSerial", sipConfig.getId()); +// result.put("SIPRealm", sipConfig.getDomain()); +// result.put("SIPHost", sipConfig.getIp()); +// result.put("SIPPort", sipConfig.getPort()); +// result.put("ChannelCount","1000"); +// result.put("VersionType",""); +// result.put("LogoMiniText",""); +// result.put("LogoText",""); +// result.put("CopyrightText",""); + + return null; + } + + /** + * 系统接口 - 登录 + * @param username 用户名 + * @param password 密码(经过md5加密,32位长度,不带中划线,不区分大小写) + * @return + */ + @RequestMapping(value = "/login") + @ResponseBody + private JSONObject login(String username,String password ){ + if (logger.isDebugEnabled()) { + logger.debug(String.format("模拟接口> 登录 API调用,username:%s ,password:%s ", + username, password)); + } + + JSONObject result = new JSONObject(); + result.put("CookieToken","ynBDDiKMg"); + result.put("URLToken","MOBkORkqnrnoVGcKIAHXppgfkNWRdV7utZSkDrI448Q.oxNjAxNTM4NDk3LCJwIjoiZGJjODg5NzliNzVj" + + "Nzc2YmU5MzBjM2JjNjg1ZWFiNGI5ZjhhN2Y0N2RlZjg3NWUyOTJkY2VkYjkwYmEwMTA0NyIsInQiOjE2MDA5MzM2OTcsInUiOiI" + + "4ODlkZDYyM2ViIn0eyJlIj.GciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJhb"); + result.put("TokenTimeout",604800); + result.put("AuthToken","MOBkORkqnrnoVGcKIAHXppgfkNWRdV7utZSkDrI448Q.oxNjAxNTM4NDk3LCJwIjoiZGJjODg5NzliNzVj" + + "Nzc2YmU5MzBjM2JjNjg1ZWFiNGI5ZjhhN2Y0N2RlZjg3NWUyOTJkY2VkYjkwYmEwMTA0NyIsInQiOjE2MDA5MzM2OTcsInUiOiI" + + "4ODlkZDYyM2ViIn0eyJlIj.GciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJhb"); + result.put("Token","ynBDDiKMg"); + return result; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiDeviceController.java b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiDeviceController.java new file mode 100644 index 0000000..64ca9f3 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiDeviceController.java @@ -0,0 +1,257 @@ +package com.yfd.monitor.web.gdw2019; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.conf.exception.ControllerException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.PresetQuerySipReq; +import com.yfd.monitor.gdw2019.transmit.callback.DeferredResultHolder; +import com.yfd.monitor.gdw2019.transmit.callback.RequestMessage; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.storager.IVideoManagerStorage; +import com.yfd.monitor.vmanager.bean.DeferredResultEx; +import com.yfd.monitor.vmanager.bean.ErrorCode; +import com.yfd.monitor.web.gdw2019.dto.DeviceChannelExtend; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.*; + +/** + * API兼容:设备信息 + */ +@SuppressWarnings("unchecked") + +@RestController +@RequestMapping(value = "/api/v1/device") +public class ApiDeviceController { + + private final static Logger logger = LoggerFactory.getLogger(ApiDeviceController.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + @Autowired + private IDeviceService deviceService; + + @Autowired + private DeferredResultHolder resultHolder; + + + /** + * 分页获取设备列表 现在直接返回,尚未实现分页 + * @param start + * @param limit + * @param q + * @param online + * @return + */ + @RequestMapping(value = "/list") + public JSONObject list( @RequestParam(required = false)Integer start, + @RequestParam(required = false)Integer limit, + @RequestParam(required = false)String q, + @RequestParam(required = false)Boolean online ){ + +// if (logger.isDebugEnabled()) { +// logger.debug("查询所有视频设备API调用"); +// } + JSONObject result = new JSONObject(); + List devices; + if (start == null || limit ==null) { + devices = storager.queryVideoDeviceList(online); + result.put("DeviceCount", devices.size()); + }else { + PageInfo deviceList = storager.queryVideoDeviceList(start/limit, limit,online); + result.put("DeviceCount", deviceList.getTotal()); + devices = deviceList.getList(); + } + + JSONArray deviceJSONList = new JSONArray(); + for (Device device : devices) { + JSONObject deviceJsonObject = new JSONObject(); + deviceJsonObject.put("ID", device.getDeviceId()); + deviceJsonObject.put("Name", device.getName()); + deviceJsonObject.put("Type", "GB"); + deviceJsonObject.put("ChannelCount", device.getChannelCount()); + deviceJsonObject.put("RecvStreamIP", ""); + deviceJsonObject.put("CatalogInterval", 3600); // 通道目录抓取周期 + deviceJsonObject.put("SubscribeInterval", device.getSubscribeCycleForCatalog()); // 订阅周期(秒), 0 表示后台不周期订阅 + deviceJsonObject.put("Online", device.getOnline() == 1); + deviceJsonObject.put("Password", ""); + deviceJsonObject.put("MediaTransport", device.getTransport()); + deviceJsonObject.put("RemoteIP", device.getIp()); + deviceJsonObject.put("RemotePort", device.getPort()); + deviceJsonObject.put("LastRegisterAt", ""); + deviceJsonObject.put("LastKeepaliveAt", ""); + deviceJsonObject.put("UpdatedAt", ""); + deviceJsonObject.put("CreatedAt", ""); + deviceJSONList.add(deviceJsonObject); + } + result.put("DeviceList",deviceJSONList); + return result; + } + + @RequestMapping(value = "/channellist") + public JSONObject channellist( String serial, + @RequestParam(required = false)String channel_type, + @RequestParam(required = false)String code , + @RequestParam(required = false)String dir_serial , + @RequestParam(required = false)Integer start, + @RequestParam(required = false)Integer limit, + @RequestParam(required = false)String q, + @RequestParam(required = false)Boolean online ){ + +// if (logger.isDebugEnabled()) { +// logger.debug("查询所有视频设备API调用"); +// } + JSONObject result = new JSONObject(); + // 查询设备是否存在 +// Device device = storager.queryVideoDevice(serial); +// if (device == null) { +// result.put("ChannelCount", 0); +// result.put("ChannelList", "[]"); +// return result; +// } + List deviceChannels; + List channelIds = null; + if (!StringUtils.isEmpty(code)) { + String[] split = code.trim().split(","); + channelIds = Arrays.asList(split); + } + List allDeviceChannelList = storager.queryChannelsByDeviceId(serial,channelIds,online); + if (start == null || limit ==null) { + deviceChannels = allDeviceChannelList; + result.put("ChannelCount", deviceChannels.size()); + }else { + deviceChannels = storager.queryChannelsByDeviceIdWithStartAndLimit(serial,channelIds, null, null, online,start, limit); + int total = allDeviceChannelList.size(); + result.put("ChannelCount", total); + } + + JSONArray channleJSONList = new JSONArray(); + for (DeviceChannelExtend deviceChannelExtend : deviceChannels) { + JSONObject deviceJOSNChannel = new JSONObject(); + deviceJOSNChannel.put("ID", deviceChannelExtend.getChannelId()); + deviceJOSNChannel.put("DeviceID", deviceChannelExtend.getDeviceId()); + deviceJOSNChannel.put("DeviceName", deviceChannelExtend.getDeviceName()); + deviceJOSNChannel.put("DeviceOnline", deviceChannelExtend.getDeviceOnline() == 1); + deviceJOSNChannel.put("Channel", 0); // TODO 自定义序号 + deviceJOSNChannel.put("Name", deviceChannelExtend.getName()); + deviceJOSNChannel.put("Custom", false); + deviceJOSNChannel.put("CustomName", ""); + deviceJOSNChannel.put("SubCount", deviceChannelExtend.getSubCount()); // TODO ? 子节点数, SubCount > 0 表示该通道为子目录 + deviceJOSNChannel.put("SnapURL", ""); + deviceJOSNChannel.put("Manufacturer ", deviceChannelExtend.getManufacture()); + deviceJOSNChannel.put("Model", deviceChannelExtend.getModel()); + deviceJOSNChannel.put("Owner", deviceChannelExtend.getOwner()); + deviceJOSNChannel.put("CivilCode", deviceChannelExtend.getCivilCode()); + deviceJOSNChannel.put("Address", deviceChannelExtend.getAddress()); + deviceJOSNChannel.put("Parental", deviceChannelExtend.getParental()); // 当为通道设备时, 是否有通道子设备, 1-有,0-没有 + deviceJOSNChannel.put("ParentID", deviceChannelExtend.getParentId()); // 直接上级编号 + deviceJOSNChannel.put("Secrecy", deviceChannelExtend.getSecrecy()); + deviceJOSNChannel.put("RegisterWay", 1); // 注册方式, 缺省为1, 允许值: 1, 2, 3 + // 1-IETF RFC3261, + // 2-基于口令的双向认证, + // 3-基于数字证书的双向认证 + deviceJOSNChannel.put("Status", deviceChannelExtend.getStatus() == 1 ? "ON":"OFF"); + deviceJOSNChannel.put("Longitude", deviceChannelExtend.getLongitude()); + deviceJOSNChannel.put("Latitude", deviceChannelExtend.getLatitude()); + deviceJOSNChannel.put("PTZType ", deviceChannelExtend.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, + // 3 - 固定枪机, 4 - 遥控枪机 + deviceJOSNChannel.put("CustomPTZType", ""); + deviceJOSNChannel.put("StreamID", deviceChannelExtend.getStreamId()); // StreamID 直播流ID, 有值表示正在直播 + deviceJOSNChannel.put("NumOutputs ", -1); // 直播在线人数 + channleJSONList.add(deviceJOSNChannel); + } + result.put("ChannelList", channleJSONList); + return result; + } + + /** + * 设备信息 - 获取下级通道预置位 + * @param serial 设备编号 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param channel 通道序号, 默认值: 1 + * @param fill 是否填充空置预置位,当下级返回预置位,但不够255个时,自动填充空置预置位到255个, 默认值: true, 允许值: true, false + * @param timeout 超时时间(秒) 默认值: 15 + * @return + */ + @RequestMapping(value = "/fetchpreset") + private DeferredResult list(String serial, + @RequestParam(required = false)Integer channel, + @RequestParam(required = false)String code, + @RequestParam(required = false)Boolean fill, + @RequestParam(required = false)Integer timeout){ + + if (logger.isDebugEnabled()) { + logger.debug("<模拟接口> 获取下级通道预置位 API调用,deviceId:{} ,channel:{} ,code:{} ,fill:{} ,timeout:{} ", + serial, channel, code, fill, timeout); + } + + Device device = storager.queryVideoDevice(serial); + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(code) ? serial : code); + DeferredResult result = new DeferredResult<> (timeout * 1000L); + DeferredResultEx deferredResultEx = new DeferredResultEx<>(result); + result.onTimeout(()->{ + logger.warn("<模拟接口> 获取设备预置位超时"); + // 释放rtpserver + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData("wait for presetquery timeout["+timeout+"s]"); + resultHolder.invokeResult(msg); + }); + if (resultHolder.exist(key, null)) { + return result; + } + + deferredResultEx.setFilter(filterResult->{ + List presetQuerySipReqList = (List)filterResult; + HashMap resultMap = new HashMap<>(); + resultMap.put("DeviceID", code); + resultMap.put("Result", "OK"); + resultMap.put("SumNum", presetQuerySipReqList.size()); + ArrayList> presetItemList = new ArrayList<>(presetQuerySipReqList.size()); + for (PresetQuerySipReq presetQuerySipReq : presetQuerySipReqList) { + Map item = new HashMap<>(); + item.put("PresetID", presetQuerySipReq.getPresetId()); + item.put("PresetName", presetQuerySipReq.getPresetName()); + item.put("PresetEnable", true); + presetItemList.add(item); + } + resultMap.put("PresetItemList",presetItemList ); + return resultMap; + }); + + resultHolder.put(key, uuid, deferredResultEx); + + try { + cmder.presetQuery(device, code, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + return result; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiStreamController.java b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiStreamController.java new file mode 100644 index 0000000..295ab2d --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/ApiStreamController.java @@ -0,0 +1,216 @@ +package com.yfd.monitor.web.gdw2019; + +import com.alibaba.fastjson2.JSONObject; +import com.yfd.monitor.common.StreamInfo; +import com.yfd.monitor.conf.UserSetting; +import com.yfd.monitor.conf.exception.SsrcTransactionNotFoundException; +import com.yfd.monitor.gdw2019.bean.Device; +import com.yfd.monitor.gdw2019.bean.DeviceChannel; +import com.yfd.monitor.gdw2019.transmit.cmd.impl.SIPCommander; +import com.yfd.monitor.media.zlm.dto.MediaServerItem; +import com.yfd.monitor.service.IDeviceService; +import com.yfd.monitor.service.IPlayService; +import com.yfd.monitor.storager.IRedisCatchStorage; +import com.yfd.monitor.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + +/** + * API兼容:实时直播 + */ +@SuppressWarnings(value = {"rawtypes", "unchecked"}) + +@RestController +@RequestMapping(value = "/api/v1/stream") +public class ApiStreamController { + + private final static Logger logger = LoggerFactory.getLogger(ApiStreamController.class); + + @Autowired + private SIPCommander cmder; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IPlayService playService; + + /** + * 实时直播 - 开始直播 + * @param serial 设备编号 + * @param channel 通道序号 默认值: 1 + * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 + * @param cdn 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent + * @param audio 是否开启音频, 默认 开启 + * @param transport 流传输模式, 默认 UDP + * @param checkchannelstatus 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 + * @param transportmode 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 + * @param timeout 拉流超时(秒), + * @return + */ + @RequestMapping(value = "/start") + private DeferredResult start(String serial , + @RequestParam(required = false)Integer channel , + @RequestParam(required = false)String code, + @RequestParam(required = false)String cdn, + @RequestParam(required = false)String audio, + @RequestParam(required = false)String transport, + @RequestParam(required = false)String checkchannelstatus , + @RequestParam(required = false)String transportmode, + @RequestParam(required = false)String timeout + + ){ + DeferredResult resultDeferredResult = new DeferredResult<>(userSetting.getPlayTimeout().longValue() + 10); + Device device = storager.queryVideoDevice(serial); + if (device == null ) { + JSONObject result = new JSONObject(); + result.put("error","device[ " + serial + " ]未找到"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + }else if (device.getOnline() == 0) { + JSONObject result = new JSONObject(); + result.put("error","device[ " + code + " ]offline"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + } + resultDeferredResult.onTimeout(()->{ + logger.info("播放等待超时"); + JSONObject result = new JSONObject(); + result.put("error","timeout"); + resultDeferredResult.setResult(result); + + // 清理RTP server + }); + + DeviceChannel deviceChannel = storager.queryChannel(serial, code); + if (deviceChannel == null) { + JSONObject result = new JSONObject(); + result.put("error","channel[ " + code + " ]未找到"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + }else if (deviceChannel.getStatus() == 0) { + JSONObject result = new JSONObject(); + result.put("error","channel[ " + code + " ]offline"); + resultDeferredResult.setResult(result); + return resultDeferredResult; + } + MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); + playService.play(newMediaServerItem, serial, code, (mediaServerItem, response)->{ + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code); + JSONObject result = new JSONObject(); + result.put("StreamID", streamInfo.getStream()); + result.put("DeviceID", device.getDeviceId()); + result.put("ChannelID", code); + result.put("ChannelName", deviceChannel.getName()); + result.put("ChannelCustomName", ""); + result.put("FLV", streamInfo.getFlv().getUrl()); + result.put("WS_FLV", streamInfo.getWs_flv().getUrl()); + result.put("RTMP", streamInfo.getRtmp().getUrl()); + result.put("HLS", streamInfo.getHls().getUrl()); + result.put("RTSP", streamInfo.getRtsp().getUrl()); + result.put("WEBRTC", streamInfo.getRtc().getUrl()); + result.put("CDN", ""); + result.put("SnapURL", ""); + result.put("Transport", device.getTransport()); + result.put("StartAt", ""); + result.put("Duration", ""); + result.put("SourceVideoCodecName", ""); + result.put("SourceVideoWidth", ""); + result.put("SourceVideoHeight", ""); + result.put("SourceVideoFrameRate", ""); + result.put("SourceAudioCodecName", ""); + result.put("SourceAudioSampleRate", ""); + result.put("AudioEnable", ""); + result.put("Ondemand", ""); + result.put("InBytes", ""); + result.put("InBitRate", ""); + result.put("OutBytes", ""); + result.put("NumOutputs", ""); + result.put("CascadeSize", ""); + result.put("RelaySize", ""); + result.put("ChannelPTZType", "0"); + resultDeferredResult.setResult(result); + }, (eventResult) -> { + JSONObject result = new JSONObject(); + result.put("error", "channel[ " + code + " ] " + eventResult.msg); + resultDeferredResult.setResult(result); + }, null); + return resultDeferredResult; + } + + /** + * 实时直播 - 直播流停止 + * @param serial 设备编号 + * @param channel 通道序号 + * @param code 通道国标编号 + * @param check_outputs + * @return + */ + @RequestMapping(value = "/stop") + @ResponseBody + private JSONObject stop(String serial , + @RequestParam(required = false)Integer channel , + @RequestParam(required = false)String code, + @RequestParam(required = false)String check_outputs + + ){ + + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code); + if (streamInfo == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到流信息"); + return result; + } + Device device = deviceService.getDevice(serial); + if (device == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到设备"); + return result; + } + try { + cmder.streamByeCmd(device, code, streamInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + JSONObject result = new JSONObject(); + result.put("error","发送BYE失败:" + e.getMessage()); + return result; + } + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + return null; + } + + /** + * 实时直播 - 直播流保活 + * @param serial 设备编号 + * @param channel 通道序号 + * @param code 通道国标编号 + * @return + */ + @RequestMapping(value = "/touch") + @ResponseBody + private JSONObject touch(String serial ,String t, + @RequestParam(required = false)Integer channel , + @RequestParam(required = false)String code, + @RequestParam(required = false)String autorestart, + @RequestParam(required = false)String audio, + @RequestParam(required = false)String cdn + ){ + return null; + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/AuthController.java b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/AuthController.java new file mode 100644 index 0000000..22c7a83 --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/AuthController.java @@ -0,0 +1,25 @@ +package com.yfd.monitor.web.gdw2019; + +import com.yfd.monitor.service.IUserService; +import com.yfd.monitor.storager.dao.dto.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping(value = "/auth") +public class AuthController { + + @Autowired + private IUserService userService; + + @RequestMapping("/login") + public String devices(String name, String passwd){ + User user = userService.getUser(name, passwd); + if (user != null) { + return "success"; + }else { + return "fail"; + } + } +} diff --git a/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/dto/DeviceChannelExtend.java b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/dto/DeviceChannelExtend.java new file mode 100644 index 0000000..3f0ff2f --- /dev/null +++ b/riis-monitor/src/main/java/com/yfd/monitor/web/gdw2019/dto/DeviceChannelExtend.java @@ -0,0 +1,555 @@ +package com.yfd.monitor.web.gdw2019.dto; + +public class DeviceChannelExtend { + + + /** + * 数据库自增ID + */ + private int id; + + /** + * 通道id + */ + private String channelId; + + /** + * 设备id + */ + private String deviceId; + + /** + * 通道名 + */ + private String name; + + private String deviceName; + + private int deviceOnline; + + /** + * 生产厂商 + */ + private String manufacture; + + /** + * 型号 + */ + private String model; + + /** + * 设备归属 + */ + private String owner; + + /** + * 行政区域 + */ + private String civilCode; + + /** + * 警区 + */ + private String block; + + /** + * 安装地址 + */ + private String address; + + /** + * 是否有子设备 1有, 0没有 + */ + private int parental; + + /** + * 父级id + */ + private String parentId; + + /** + * 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式 + */ + private int safetyWay; + + /** + * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式 + */ + private int registerWay; + + /** + * 证书序列号 + */ + private String certNum; + + /** + * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 + */ + private int certifiable; + + /** + * 证书无效原因码 + */ + private int errCode; + + /** + * 证书终止有效期 + */ + private String endTime; + + /** + * 保密属性 缺省为0; 0:不涉密, 1:涉密 + */ + private String secrecy; + + /** + * IP地址 + */ + private String ipAddress; + + /** + * 端口号 + */ + private int port; + + /** + * 密码 + */ + private String password; + + /** + * 云台类型 + */ + private int PTZType; + + /** + * 云台类型描述字符串 + */ + private String PTZTypeText; + + /** + * 创建时间 + */ + private String createTime; + + /** + * 更新时间 + */ + private String updateTime; + + /** + * 在线/离线 + * 1在线,0离线 + * 默认在线 + * 信令: + * ON + * OFF + * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF + */ + private int status; + + /** + * 经度 + */ + private double longitude; + + /** + * 纬度 + */ + private double latitude; + + /** + * 经度 GCJ02 + */ + private double longitudeGcj02; + + /** + * 纬度 GCJ02 + */ + private double latitudeGcj02; + + /** + * 经度 WGS84 + */ + private double longitudeWgs84; + + /** + * 纬度 WGS84 + */ + private double latitudeWgs84; + + /** + * 子设备数 + */ + private int subCount; + + /** + * 流唯一编号,存在表示正在直播 + */ + private String streamId; + + /** + * 是否含有音频 + */ + private boolean hasAudio; + + /** + * 标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 + */ + private int channelType; + + /** + * 业务分组 + */ + private String businessGroupId; + + /** + * GPS的更新时间 + */ + private String gpsTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public void setPTZType(int PTZType) { + this.PTZType = PTZType; + switch (PTZType) { + case 0: + this.PTZTypeText = "未知"; + break; + case 1: + this.PTZTypeText = "球机"; + break; + case 2: + this.PTZTypeText = "半球"; + break; + case 3: + this.PTZTypeText = "固定枪机"; + break; + case 4: + this.PTZTypeText = "遥控枪机"; + break; + } + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getManufacture() { + return manufacture; + } + + public void setManufacture(String manufacture) { + this.manufacture = manufacture; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getCivilCode() { + return civilCode; + } + + public void setCivilCode(String civilCode) { + this.civilCode = civilCode; + } + + public String getBlock() { + return block; + } + + public void setBlock(String block) { + this.block = block; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getParental() { + return parental; + } + + public void setParental(int parental) { + this.parental = parental; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public int getSafetyWay() { + return safetyWay; + } + + public void setSafetyWay(int safetyWay) { + this.safetyWay = safetyWay; + } + + public int getRegisterWay() { + return registerWay; + } + + public void setRegisterWay(int registerWay) { + this.registerWay = registerWay; + } + + public String getCertNum() { + return certNum; + } + + public void setCertNum(String certNum) { + this.certNum = certNum; + } + + public int getCertifiable() { + return certifiable; + } + + public void setCertifiable(int certifiable) { + this.certifiable = certifiable; + } + + public int getErrCode() { + return errCode; + } + + public void setErrCode(int errCode) { + this.errCode = errCode; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public String getSecrecy() { + return secrecy; + } + + public void setSecrecy(String secrecy) { + this.secrecy = secrecy; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getPTZType() { + return PTZType; + } + + public String getPTZTypeText() { + return PTZTypeText; + } + + public void setPTZTypeText(String PTZTypeText) { + this.PTZTypeText = PTZTypeText; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getLongitudeGcj02() { + return longitudeGcj02; + } + + public void setLongitudeGcj02(double longitudeGcj02) { + this.longitudeGcj02 = longitudeGcj02; + } + + public double getLatitudeGcj02() { + return latitudeGcj02; + } + + public void setLatitudeGcj02(double latitudeGcj02) { + this.latitudeGcj02 = latitudeGcj02; + } + + public double getLongitudeWgs84() { + return longitudeWgs84; + } + + public void setLongitudeWgs84(double longitudeWgs84) { + this.longitudeWgs84 = longitudeWgs84; + } + + public double getLatitudeWgs84() { + return latitudeWgs84; + } + + public void setLatitudeWgs84(double latitudeWgs84) { + this.latitudeWgs84 = latitudeWgs84; + } + + public int getSubCount() { + return subCount; + } + + public void setSubCount(int subCount) { + this.subCount = subCount; + } + + public boolean isHasAudio() { + return hasAudio; + } + + public void setHasAudio(boolean hasAudio) { + this.hasAudio = hasAudio; + } + + public String getStreamId() { + return streamId; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + public String getCreateTime() { + return createTime; + } + + public void setCreateTime(String createTime) { + this.createTime = createTime; + } + + public String getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(String updateTime) { + this.updateTime = updateTime; + } + + public int getChannelType() { + return channelType; + } + + public void setChannelType(int channelType) { + this.channelType = channelType; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } + + public String getGpsTime() { + return gpsTime; + } + + public void setGpsTime(String gpsTime) { + this.gpsTime = gpsTime; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public int getDeviceOnline() { + return deviceOnline; + } + + public void setDeviceOnline(int deviceOnline) { + this.deviceOnline = deviceOnline; + } +} diff --git a/riis-monitor/src/main/resources/all-application.yml b/riis-monitor/src/main/resources/all-application.yml new file mode 100644 index 0000000..2f020cf --- /dev/null +++ b/riis-monitor/src/main/resources/all-application.yml @@ -0,0 +1,187 @@ + +# 此配置文件只是用作展示所有配置项, 不可直接使用 +spring: + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 6 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: + # [可选] 超时时间 + timeout: 10000 + # [可选] 一个pool最多可分配多少个jedis实例 + poolMaxTotal: 1000 + # [可选] 一个pool最多有多少个状态为idle(空闲)的jedis实例 + poolMaxIdle: 500 + # [可选] 最大的等待时间(秒) + poolMaxWait: 5 + # [必选] jdbc数据库配置 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + username: root + password: root123 + hikari: + connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 + initialSize: 10 # 连接池初始化连接数 + maximum-pool-size: 200 # 连接池最大连接数 + minimum-idle: 5 # 连接池最小空闲连接数 + idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) + max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) + + +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + # [可选] HTTPS配置, 默认不开启 + ssl: + # [可选] 是否开启HTTPS访问 + enabled: false + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 + key-store: classpath:xxx.jks + # [可选] 证书密码 + key-store-password: password + # [可选] 证书类型, 默认为jks,根据实际修改 + key-store-type: JKS + # 配置证书可以使用如下两项,如上面二选一即可 + # PEM 编码证书 + certificate: xx.pem + # 私钥文件 + certificate-private-key: xx.key + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP + ip: 192.168.0.100 + # [可选] 没有任何业务需求,仅仅是在前端展示的时候用 + show-ip: 192.168.0.100 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 4401020049 + # [可选] + id: 44010200492000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: admin123 + # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 + register-time-interval: 60 + # [可选] 云台控制速度 + ptz-speed: 50 + # TODO [可选] 收到心跳后自动上线, 重启服务后会将所有设备置为离线,默认false,等待注册后上线。设置为true则收到心跳设置为上线。 + # keepalliveToOnline: false + # 是否存储alarm信息 + alarm: false + +#zlm 默认服务器配置 +media: + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: + # [必须修改] zlm服务器的内网IP + ip: 192.168.0.100 + # [可选] 返回流地址时的ip,置空使用 media.ip + stream-ip: + # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip + sdp-ip: + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip + hook-ip: + # [必须修改] zlm服务器的http.port + http-port: 80 + # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 + http-ssl-port: + # [可选] zlm服务器的rtmp.port, 置空使用zlm配置文件配置 + rtmp-port: + # [可选] zlm服务器的rtmp.sslport, 置空使用zlm配置文件配置 + rtmp-ssl-port: + # [可选] zlm服务器的 rtp_proxy.port, 置空使用zlm配置文件配置 + rtp-proxy-port: + # [可选] zlm服务器的 rtsp.port, 置空使用zlm配置文件配置 + rtsp-port: + # [可选] zlm服务器的 rtsp.sslport, 置空使用zlm配置文件配置 + rtsp-ssl-port: + # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 + auto-config: true + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 + port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 0 + +# [可选] 日志配置, 一般不需要改 +logging: + config: classpath:logback-spring-local.xml + +# [根据业务需求配置] +user-settings: + # [可选] 服务ID,不写则为000000 + server-id: + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: false + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播/录像回放 等待超时时间,单位:毫秒 + play-timeout: 18000 + # 上级点播等待超时时间,单位:毫秒 + platform-play-timeout: 60000 + # 是否开启接口鉴权 + interface-authentication: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: true + # 国标是否录制 + record-sip: true + # 是否将日志存储进数据库 + logInDatebase: true + # 使用推流状态作为推流通道状态 + use-pushing-as-status: true + # 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启 + use-source-ip-as-stream-ip: true + # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + stream-on-demand: true + # 推流鉴权, 默认开启 + push-authority: true + # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能, + # 非严格模式使用随机端口发流,性能更好, 默认关闭 + gb-send-stream-strict: false + # 设备上线时是否自动同步通道 + sync-channel-on-device-online: false + # 是否使用设备来源Ip作为回复IP, 不设置则为 false + sip-use-source-ip-as-remote-address: false + # 是否开启sip日志 + sip-log: true + # 消息通道功能-缺少国标ID是否给所有上级发送消息 + send-to-platforms-when-id-lost: true + # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息 + refuse-channel-status-channel-form-notify: false + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 + allowed-origins: + - http://localhost:8008 + - http://192.168.1.3:8008 + +# 关闭在线文档(生产环境建议关闭) +springdoc: + api-docs: + enabled: false + swagger-ui: + enabled: false diff --git a/riis-monitor/src/main/resources/application-dev.yml b/riis-monitor/src/main/resources/application-dev.yml new file mode 100644 index 0000000..9b4cc90 --- /dev/null +++ b/riis-monitor/src/main/resources/application-dev.yml @@ -0,0 +1,157 @@ +#[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + appname: + + +spring: + + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + #host: 127.0.0.1 #82.156.18.154 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 8 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + #password: ZGHszy741230 #123456 + # [可选] 超时时间 + timeout: 1000000 + # mysql数据源 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://43.138.168.68:3306/smartsubstationdb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: ylfw20230626@ #ylfw20230626@ +# url: jdbc:mysql://192.168.1.119:3308/riisdb500?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true +# username: root +# password: 123456 + hikari: + connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 + initialSize: 10 # 连接池初始化连接数 + maximum-pool-size: 200 # 连接池最大连接数 + minimum-idle: 5 # 连接池最小空闲连接数 + idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) + max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) + + + + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP + ip: 192.168.1.20 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 3402000000 + # [可选] + id: 34020000002000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: 12345678 + +#zlm 默认服务器配置 +media: + id: riis-mediaServer + # [必须修改] zlm服务器的内网IP + ip: 192.168.1.20 + # [必须修改] zlm服务器的http.port + http-port: 81 + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc #QUdZ1Gk4axB57kVvn0UKnzdGGHvpROvI + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 + port-range: 30000,30500 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 0 +# [可选] 日志配置, 一般不需要改 +logging: + config: classpath:logback-spring-local.xml + +# [根据业务需求配置] +user-settings: + # [可选] 服务ID,不写则为000000 + server-id: StationArea001 + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true 非常重要的设置(zhengsl at 2023-04-16) + auto-apply-play: true + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播/录像回放 等待超时时间,单位:毫秒 + play-timeout: 18000 + + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 + wait-track: false + # 上级点播等待超时时间,单位:毫秒 + platform-play-timeout: 60000 + # 是否开启接口鉴权 + interface-authentication: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: false + # 国标是否录制 + record-sip: false + # 是否将日志存储进数据库 + logInDatebase: true + # 使用推流状态作为推流通道状态 + use-pushing-as-status: true + # 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启 + use-source-ip-as-stream-ip: false + # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + stream-on-demand: true + # 推流鉴权, 默认开启 + push-authority: true + # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能, + # 非严格模式使用随机端口发流,性能更好, 默认关闭 + gb-send-stream-strict: false + # 设备上线时是否自动同步通道 + sync-channel-on-device-online: true + # 是否使用设备来源Ip作为回复IP, 不设置则为 false + sip-use-source-ip-as-remote-address: false + # 是否开启sip日志 + sip-log: false + # 消息通道功能-缺少国标ID是否给所有上级发送消息 + send-to-platforms-when-id-lost: true + # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息 + refuse-channel-status-channel-form-notify: false + + # 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认10000 + max-notify-count-queue: 10000 + + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 + allowed-origins: + - http://localhost:3000 + - http://192.168.1.245:3001 + - http://192.168.1.112:3002 + - http://192.168.1.211:3001 + - http://192.168.1.20:3000 + - http://192.168.1.112:18080 + - http://192.168.1.38:3000 + - http://192.168.1.8:3000 + # 项目中扩展辅助设置 + station: + id: 9b4e9d9f70b2256a69dfcbdecb13c960 + staion_code: 500KV00001 + staion_name: 500Kv标准验证变电站 + snapfilepath: d:\riis\parent-snapimage\ #上级单位视频截图保存路径 + ffmpegpath: E:\ffmpeg\bin\ #ffmpeg 工具目录 diff --git a/riis-monitor/src/main/resources/application-prod.yml b/riis-monitor/src/main/resources/application-prod.yml new file mode 100644 index 0000000..ff490d9 --- /dev/null +++ b/riis-monitor/src/main/resources/application-prod.yml @@ -0,0 +1,156 @@ +#[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + appname: + + +spring: + + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + #host: 127.0.0.1 #82.156.18.154 + host: 192.168.66.2 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 5 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + #password: ZGHszy741230 #123456 + # [可选] 超时时间 + timeout: 1000000 + # mysql数据源 + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.66.2:3306/riisdb500_zhuangzhou?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: root + password: ylfw20230626@ #ylfw20230626@ +# url: jdbc:mysql://192.168.1.119:3308/riisdb500?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true +# username: root +# password: 123456 + hikari: + connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 + initialSize: 10 # 连接池初始化连接数 + maximum-pool-size: 200 # 连接池最大连接数 + minimum-idle: 5 # 连接池最小空闲连接数 + idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) + max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) + + + + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP + ip: 192.168.66.2 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 3402000000 + # [可选] + id: 34020000002000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: 12345678 + +#zlm 默认服务器配置 +media: + id: riis-mediaServer + # [必须修改] zlm服务器的内网IP + ip: 192.168.66.2 + # [必须修改] zlm服务器的http.port + http-port: 81 + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc #QUdZ1Gk4axB57kVvn0UKnzdGGHvpROvI + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 + port-range: 30000,30500 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 0 +# [可选] 日志配置, 一般不需要改 +logging: + config: classpath:logback-spring-local.xml + +# [根据业务需求配置] +user-settings: + # [可选] 服务ID,不写则为000000 + server-id: StationArea001 + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true 非常重要的设置(zhengsl at 2023-04-16) + auto-apply-play: true + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播/录像回放 等待超时时间,单位:毫秒 + play-timeout: 18000 + + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 + wait-track: false + # 上级点播等待超时时间,单位:毫秒 + platform-play-timeout: 60000 + # 是否开启接口鉴权 + interface-authentication: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: false + # 国标是否录制 + record-sip: false + # 是否将日志存储进数据库 + logInDatebase: true + # 使用推流状态作为推流通道状态 + use-pushing-as-status: true + # 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启 + use-source-ip-as-stream-ip: false + # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + stream-on-demand: true + # 推流鉴权, 默认开启 + push-authority: true + # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能, + # 非严格模式使用随机端口发流,性能更好, 默认关闭 + gb-send-stream-strict: false + # 设备上线时是否自动同步通道 + sync-channel-on-device-online: true + # 是否使用设备来源Ip作为回复IP, 不设置则为 false + sip-use-source-ip-as-remote-address: false + # 是否开启sip日志 + sip-log: false + # 消息通道功能-缺少国标ID是否给所有上级发送消息 + send-to-platforms-when-id-lost: true + # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息 + refuse-channel-status-channel-form-notify: false + + # 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认10000 + max-notify-count-queue: 10000 + + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 + allowed-origins: + - http://localhost:3000 + - http://192.168.66.2:3001 + - http://192.168.66.2:3000 + - http://192.168.1.3:3000 + - http://192.168.66.2:18080 + - http://192.168.1.38:3000 + - http://192.168.1.8:3000 + # 项目中扩展辅助设置 + station: + id: 9b4e9d9f70b2256a69dfcbdecb13c960 + staion_code: 500KV00001 + staion_name: 500Kv标准验证变电站 + snapfilepath: d:\riis\parent-snapimage\ #上级单位视频截图保存路径 + ffmpegpath: E:\ffmpeg\bin\ #ffmpeg 工具目录 diff --git a/riis-monitor/src/main/resources/application.yml b/riis-monitor/src/main/resources/application.yml new file mode 100644 index 0000000..d66dd77 --- /dev/null +++ b/riis-monitor/src/main/resources/application.yml @@ -0,0 +1,8 @@ +spring: + profiles: + active: dev + config: + name: 配电网变电站智能化平台 + +java: + opts: -Xmx2048m -Xms1024m diff --git a/riis-monitor/src/main/resources/logback-spring-local.xml b/riis-monitor/src/main/resources/logback-spring-local.xml new file mode 100644 index 0000000..cf7e211 --- /dev/null +++ b/riis-monitor/src/main/resources/logback-spring-local.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + ${log.pattern} + UTF-8 + + + + + DEBUG + + + + + + + + + + ${LOG_HOME}/wvp-%d{yyyy-MM-dd}.%i.log + + 30 + 20MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + UTF-8 + + + + + info + + + + + + + + + ${LOG_HOME}/error-%d{yyyy-MM-dd}.%i.log + + 30 + 20MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + UTF-8 + + + + WARN + + + + + + + + ${LOG_HOME}/sip-%d{yyyy-MM-dd}.%i.log + + 30 + 50MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + UTF-8 + + + + + + + + + + + + + + + + + + + diff --git a/riis-monitor/src/main/resources/static/crossdomain.xml b/riis-monitor/src/main/resources/static/crossdomain.xml new file mode 100644 index 0000000..7c8b941 --- /dev/null +++ b/riis-monitor/src/main/resources/static/crossdomain.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/riis-monitor/src/main/resources/static/index.html b/riis-monitor/src/main/resources/static/index.html new file mode 100644 index 0000000..4fe21a1 --- /dev/null +++ b/riis-monitor/src/main/resources/static/index.html @@ -0,0 +1 @@ +国标28181
\ No newline at end of file diff --git a/riis-monitor/src/main/resources/static/liveplayer.swf b/riis-monitor/src/main/resources/static/liveplayer.swf new file mode 100644 index 0000000..f78bbe5 Binary files /dev/null and b/riis-monitor/src/main/resources/static/liveplayer.swf differ diff --git a/riis-monitor/src/main/resources/wvpssl.jks b/riis-monitor/src/main/resources/wvpssl.jks new file mode 100644 index 0000000..92b98b7 Binary files /dev/null and b/riis-monitor/src/main/resources/wvpssl.jks differ diff --git a/riis-system/pom.xml b/riis-system/pom.xml new file mode 100644 index 0000000..7394daa --- /dev/null +++ b/riis-system/pom.xml @@ -0,0 +1,275 @@ + + + 4.0.0 + + riis-system + 业务系统模块 + war + + + com.jytech + riis + 2.7 + + + + 1.8 + + + + + + com.google.zxing + javase + 3.5.0 + + + com.google.zxing + core + 3.5.0 + + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + com.squareup.okhttp3 + logging-interceptor + + + org.junit.jupiter + junit-jupiter-api + + + + it.sauronsoftware + ftp4j + 1.7.2 + + + + org.apache.poi + poi + 4.1.2 + + + org.apache.poi + poi-ooxml + 4.1.2 + + + + + com.deepoove + poi-tl + 1.10.0 + + + + + org.bytedeco + javacv + 1.4.3 + + + org.bytedeco.javacpp-presets + ffmpeg-platform + 4.0.2-1.4.3 + + + + net.coobird + thumbnailator + 0.4.19 + + + + + + + + + + + + + + + + + + net.java.dev.jna + jna + 5.12.1 + + + net.java.dev.jna + jna-platform + 5.12.1 + + + + + com.penn + examples.jar + 0.0.1-SNAPSHOT + system + ${project.basedir}/src/main/resources/lib/examples.jar + + + com.alibaba.fastjson2 + fastjson2-extension + 2.0.17 + compile + + + + com.alibaba.fastjson2 + fastjson2-extension + 2.0.17 + compile + + + + + + org.apache.ftpserver + ftpserver-core + 1.2.0 + + + org.apache.ftpserver + ftplet-api + 1.2.0 + + + + org.apache.mina + mina-core + 2.0.16 + + + + + commons-net + commons-net + 3.3 + + + + + io.netty + netty-all + 4.1.90.Final + + + + + org.springframework.integration + spring-integration-mqtt + 5.5.14 + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.2 + + + + + org.bouncycastle + bcprov-jdk15to18 + 1.66 + + + + org.springframework.boot + spring-boot-starter-mail + + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.12.5 + + + + io.minio + minio + 8.4.3 + + + + + com.tencentcloudapi + tencentcloud-sdk-java + 3.1.62 + + + + + + + + + src/main/resources + + **/*.* + + false + + + src/main/java + + **/*.* + + + **/*.java + + false + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.8 + + + generate-docs + prepare-package + + process-asciidoc + + + html + book + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.3 + + true + + + org.projectlombok + lombok + + + + + + + + diff --git a/riis-system/src/main/java/com/yfd/platform/PlatformApplication.java b/riis-system/src/main/java/com/yfd/platform/PlatformApplication.java new file mode 100644 index 0000000..fc4f82f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/PlatformApplication.java @@ -0,0 +1,53 @@ +package com.yfd.platform; + +import com.yfd.platform.annotation.rest.AnonymousGetMapping; +import com.yfd.platform.utils.SpringContextHolder; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.web.bind.annotation.RestController; + +/** + * + * //@SpringBootApplication + */ +@RestController +@EnableTransactionManagement +@EnableAsync +@ServletComponentScan("com.yfd.platform.config") +@MapperScan(basePackages = "com.yfd.platform.modules.*.mapper,com.yfd.platform.*.mapper") +@SpringBootApplication +@EnableScheduling +@EnableCaching +@EnableGlobalMethodSecurity(prePostEnabled=true) +public class PlatformApplication { + + public static void main(String[] args) { + SpringApplication.run(PlatformApplication.class, args); + } + + @Bean + public SpringContextHolder springContextHolder() { + return new SpringContextHolder(); + } + + static { + System.setProperty("druid.mysql.usePingMethod","false"); + } + /** + * 访问首页提示 + * + * @return / + */ + @AnonymousGetMapping("/") + public String index() { + return "Backend service started successfully"; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/ServletInitializer.java b/riis-system/src/main/java/com/yfd/platform/ServletInitializer.java new file mode 100644 index 0000000..0234b48 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/ServletInitializer.java @@ -0,0 +1,13 @@ +package com.yfd.platform; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +public class ServletInitializer extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(PlatformApplication.class); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/annotation/AnonymousAccess.java b/riis-system/src/main/java/com/yfd/platform/annotation/AnonymousAccess.java new file mode 100644 index 0000000..4e6e096 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/annotation/AnonymousAccess.java @@ -0,0 +1,29 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.annotation; + +import java.lang.annotation.*; + +/** + * 用于标记匿名访问方法 + */ +@Inherited +@Documented +@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AnonymousAccess { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/annotation/Log.java b/riis-system/src/main/java/com/yfd/platform/annotation/Log.java new file mode 100644 index 0000000..db998eb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/annotation/Log.java @@ -0,0 +1,22 @@ +package com.yfd.platform.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @date 2018-11-24 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Log { + + String value() default ""; + + String module() default ""; + + String type() default ""; +} + diff --git a/riis-system/src/main/java/com/yfd/platform/annotation/rest/AnonymousGetMapping.java b/riis-system/src/main/java/com/yfd/platform/annotation/rest/AnonymousGetMapping.java new file mode 100644 index 0000000..c5b178e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/annotation/rest/AnonymousGetMapping.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yfd.platform.annotation.rest; + +import com.yfd.platform.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.lang.annotation.*; + +/** + * Annotation for mapping HTTP {@code GET} requests onto specific handler + * methods. + *

+ * 支持匿名访问 GetMapping + * + * @see RequestMapping + */ +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.GET) +public @interface AnonymousGetMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + * + * @since 4.3.5 + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/aspect/LogAspect.java b/riis-system/src/main/java/com/yfd/platform/aspect/LogAspect.java new file mode 100644 index 0000000..7642c3f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/aspect/LogAspect.java @@ -0,0 +1,92 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.aspect; + +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.RequestHolder; +import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * + * @date 2018-11-24 + */ +@Component +@Aspect +@Slf4j +public class LogAspect { + + @Resource + private final ISysLogService sysLogService; + + @Resource + private IUserService userService; + + ThreadLocal currentTime = new ThreadLocal<>(); + + public LogAspect(ISysLogService sysLogService) { + this.sysLogService = sysLogService; + } + + /** + * 配置切入点 + */ + @Pointcut("@annotation(com.yfd.platform.annotation.Log)") + public void logPointcut() { + // 该方法无方法体,主要为了让同类中其他方法使用此切入点 + } + + /** + * 配置环绕通知,使用在方法logPointcut()上注册的切入点 + * + * @param joinPoint join point for advice + */ + @Around("logPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + Object result; + currentTime.set(System.currentTimeMillis()); + result = joinPoint.proceed(); + SysLog log = new SysLog("INFO"); + currentTime.remove(); + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + Map nameInfo = userService.getNameInfo(); + String nickname = nameInfo.get("nickname"); + String username = nameInfo.get("username"); + sysLogService.save(nickname, username, StringUtils.getBrowser(request), + StringUtils.getIpAddr(request), joinPoint, log); + return result; + } + + public String getUsername() { + try { + return SecurityUtils.getCurrentUsername(); + } catch (Exception e) { + return ""; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/ServerSendEventServer.java b/riis-system/src/main/java/com/yfd/platform/component/ServerSendEventServer.java new file mode 100644 index 0000000..80ee461 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/ServerSendEventServer.java @@ -0,0 +1,134 @@ +package com.yfd.platform.component; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +/** + * SSE Server send Event 服务器推送服务 + */ +@Slf4j +public class ServerSendEventServer { + + /** + * 当前连接数 + */ + private static AtomicInteger count = new AtomicInteger(0); + + private static Map sseEmitterMap = new ConcurrentHashMap<>(); + + public static SseEmitter connect(String userId){ + //设置超时时间,0表示不过期,默认是30秒,超过时间未完成会抛出异常 + SseEmitter sseEmitter = new SseEmitter(0L); + //SseEmitter sseEmitter = new SseEmitter(); + //注册回调 + sseEmitter.onCompletion(completionCallBack(userId)); + sseEmitter.onError(errorCallBack(userId)); + sseEmitter.onTimeout(timeOutCallBack(userId)); + sseEmitterMap.put(userId,sseEmitter); + //数量+1 + count.getAndIncrement(); + log.info("create new sse connect ,current user:{}",userId); + return sseEmitter; + } + /** + * 给指定用户发消息 + */ + public static void sendMessage(String userId, String message){ + if(sseEmitterMap.containsKey(userId)){ + try{ + sseEmitterMap.get(userId).send(message); + }catch (IOException e){ + log.error("user id:{}, send message error:{}",userId,e.getMessage()); + log.error(e.getMessage()); + } + } + } + + /** + * 给所有用户发消息 + */ + public static void sendMessage(String message) { + if (sseEmitterMap != null && !sseEmitterMap.isEmpty()) { + sseEmitterMap.forEach((k, v) -> { + // 发送消息 + sendMessage(k, message); + + }); + } + } + + /** + * 想多人发送消息,组播 + */ + public static void groupSendMessage(String groupId, String message){ + if(sseEmitterMap!=null&&!sseEmitterMap.isEmpty()){ + sseEmitterMap.forEach((k,v) -> { + try{ + if(k.startsWith(groupId)){ + v.send(message, MediaType.APPLICATION_JSON); + } + }catch (IOException e){ + log.error("user id:{}, send message error:{}",groupId,message); + removeUser(k); + } + }); + } + } + public static void batchSendMessage(String message) { + sseEmitterMap.forEach((k,v)->{ + try{ + v.send(message, MediaType.APPLICATION_JSON); + }catch (IOException e){ + log.error("user id:{}, send message error:{}",k,e.getMessage()); + removeUser(k); + } + }); + } + /** + * 群发消息 + */ + public static void batchSendMessage(String message, Set userIds){ + userIds.forEach(userId->sendMessage(userId,message)); + } + public static void removeUser(String userId){ + sseEmitterMap.remove(userId); + //数量-1 + count.getAndDecrement(); + log.info("remove user id:{}",userId); + } + + public static List getIds(){ + return new ArrayList<>(sseEmitterMap.keySet()); + } + public static int getUserCount(){ + return count.intValue(); + } + private static Runnable completionCallBack(String userId) { + return () -> { + log.info("结束连接,{}",userId); + removeUser(userId); + }; + } + private static Runnable timeOutCallBack(String userId){ + return ()->{ + log.info("连接超时,{}",userId); + removeUser(userId); + }; + } + private static Consumer errorCallBack(String userId){ + return throwable -> { + log.error("连接异常,{}",userId); + removeUser(userId); + }; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/WebSocketServer.java b/riis-system/src/main/java/com/yfd/platform/component/WebSocketServer.java new file mode 100644 index 0000000..10dd581 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/WebSocketServer.java @@ -0,0 +1,125 @@ +package com.yfd.platform.component; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.PathParam; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.CopyOnWriteArrayList; + +@ServerEndpoint("/websocket/{token}") +@Component +@Slf4j +public class WebSocketServer { + private static int onlineCount=0;//在线人数 + private static CopyOnWriteArrayList webSocketSet=new CopyOnWriteArrayList();//在线用户集合 + private Session session;//与某个客户端的连接会话 + private String currentUser; + + @OnOpen + public void onOpen(@PathParam("token") String token, Session session){ + this.currentUser = token; + this.session=session; + webSocketSet.add(this);//加入set中 + addOnlineCount(); + log.info("有新连接加入!当前在线人数为"+getOnlineCount()); + allCurrentOnline(); + } + + @OnClose + public void onClose(){ + webSocketSet.remove(this); + subOnlineCount(); + log.info("有一连接关闭!当前在线人数为" + getOnlineCount()); + allCurrentOnline(); + } + + @OnMessage + public void onMessage(String message, Session session){ + log.info("来自客户端的消息:"+message); + for (WebSocketServer item:webSocketSet){ + try { + item.sendMessage(message); + } catch (IOException e) { + log.error(e.getMessage()); + continue; + } + } + } + + @OnError + public void onError(Session session, Throwable throwable){ + log.info("发生错误!"); + log.error(throwable.getMessage()); + } + + public void sendMessage(String message) throws IOException { + this.session.getBasicRemote().sendText(message); + } + + /** + * 获取当前所有在线用户名 + */ + public static void allCurrentOnline(){ + for (WebSocketServer item : webSocketSet) { + log.info(item.currentUser); + } + } + + /** + * 发送给指定用户 + */ + public static void sendMessageTo(String message,String token) throws IOException { + for (WebSocketServer item : webSocketSet) { + if(item.currentUser.equals(token)){ + item.session.getBasicRemote().sendText(message); + } + } + } + + /** + * 群发自定义消息 + */ + public static void sendInfo(String message) throws IOException { + log.info(message); + for (WebSocketServer item : webSocketSet) { + try { + item.sendMessage(message); + } catch (IOException e) { + continue; + } + } + } + + /** + * 群发自定义消息 + */ + public static void sendInfo(String token,String message) { + for (WebSocketServer item : webSocketSet) { + try { + if(item.currentUser.startsWith(token)){ + item.sendMessage(message); + } + + } catch (IOException e) { + continue; + } + } + } + + + public static synchronized int getOnlineCount(){ + return onlineCount; + } + public static synchronized void addOnlineCount(){ + WebSocketServer.onlineCount++; + } + public static synchronized void subOnlineCount(){ + WebSocketServer.onlineCount--; + } + +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttConfig.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttConfig.java new file mode 100644 index 0000000..0e1c112 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttConfig.java @@ -0,0 +1,97 @@ +package com.yfd.platform.component.mqtt; + +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.channel.ExecutorChannel; +import org.springframework.integration.dsl.IntegrationFlow; +import org.springframework.integration.dsl.IntegrationFlows; +import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; +import org.springframework.integration.mqtt.core.MqttPahoClientFactory; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; +import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * MqttConfigV2 + * + * @date 2022/8/24 + */ +//@Configuration +public class MqttConfig { + + @Autowired + private MqttProperties mqttProperties; + + @Autowired + private MqttMessageHandle mqttMessageHandle; + + + //Mqtt 客户端工厂 所有客户端从这里产生 + @Bean + public MqttPahoClientFactory mqttPahoClientFactory(){ + DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); + MqttConnectOptions options = new MqttConnectOptions(); + options.setServerURIs(mqttProperties.getHostUrl().split(",")); + options.setUserName(mqttProperties.getUsername()); + options.setPassword(mqttProperties.getPassword().toCharArray()); + factory.setConnectionOptions(options); + return factory; + } + + // Mqtt 管道适配器 + @Bean + public MqttPahoMessageDrivenChannelAdapter adapter(MqttPahoClientFactory factory){ + return new MqttPahoMessageDrivenChannelAdapter(mqttProperties.getInClientId(),factory,mqttProperties.getDefaultTopic().split(",")); + } + + // 消息消费者 (接收,处理来自mqtt的消息) + @Bean + public IntegrationFlow mqttInbound(MqttPahoMessageDrivenChannelAdapter adapter) { + adapter.setCompletionTimeout(5000); + adapter.setQos(1); + return IntegrationFlows.from( adapter) + .channel(new ExecutorChannel(mqttThreadPoolTaskExecutor())) + .handle(mqttMessageHandle) + .get(); + } + + @Bean + public ThreadPoolTaskExecutor mqttThreadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + // 最大可创建的线程数 + int maxPoolSize = 200; + executor.setMaxPoolSize(maxPoolSize); + // 核心线程池大小 + int corePoolSize = 50; + executor.setCorePoolSize(corePoolSize); + // 队列最大长度 + int queueCapacity = 1000; + executor.setQueueCapacity(queueCapacity); + // 线程池维护线程所允许的空闲时间 + int keepAliveSeconds = 300; + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + // 出站处理器 (向 mqtt 发送消息 生产者) + @Bean + public IntegrationFlow mqttOutboundFlow(MqttPahoClientFactory factory) { + + MqttPahoMessageHandler handler = new MqttPahoMessageHandler(mqttProperties.getOutClientId(),factory); + handler.setAsync(true); + handler.setConverter(new DefaultPahoMessageConverter()); + handler.setDefaultTopic(mqttProperties.getDefaultTopic().split(",")[0]); + return IntegrationFlows.from( "mqttOutboundChannel").handle(handler).get(); + } + +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttGateway.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttGateway.java new file mode 100644 index 0000000..787ceb8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttGateway.java @@ -0,0 +1,15 @@ +package com.yfd.platform.component.mqtt; + +import org.springframework.integration.annotation.MessagingGateway; +import org.springframework.integration.mqtt.support.MqttHeaders; +import org.springframework.messaging.handler.annotation.Header; +import org.springframework.stereotype.Component; + +@Component +@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") +public interface MqttGateway { + + void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String data); + + void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) Integer Qos, String data); +} \ No newline at end of file diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttMessageHandle.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttMessageHandle.java new file mode 100644 index 0000000..20a5e89 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttMessageHandle.java @@ -0,0 +1,135 @@ +package com.yfd.platform.component.mqtt; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessagingException; +import org.springframework.stereotype.Component; +import com.yfd.platform.utils.SpringUtils; +import javax.annotation.PostConstruct; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * MessageHandleService + * + * @date 2022/8/24 + */ +@Component +public class MqttMessageHandle implements MessageHandler { + + public static final Logger log = LoggerFactory.getLogger(MqttMessageHandle.class); + + // 包含 @MqttService注解 的类(Component) + public static Map mqttServices; + + /** + * 所有mqtt到达的消息都会在这里处理 + * 要注意这个方法是在线程池里面运行的 + * @param message message + */ + @Override + public void handleMessage(Message message) throws MessagingException { + getMqttTopicService(message); + } + + public Map getMqttServices(){ + if(mqttServices==null){ + mqttServices = SpringUtils.getBeansByAnnotation(MqttService.class); + } + return mqttServices; + } + + public void getMqttTopicService(Message message){ + // 在这里 我们根据不同的 主题 分发不同的消息 + String receivedTopic = message.getHeaders().get("mqtt_receivedTopic",String.class); + if(receivedTopic==null || "".equals(receivedTopic)){ + return; + } + for(Map.Entry entry : getMqttServices().entrySet()){ + // 把所有带有 @MqttService 的类遍历 + Class clazz = entry.getValue().getClass(); + // 获取他所有方法 + Method[] methods = clazz.getDeclaredMethods(); + for ( Method method: methods ){ + if (method.isAnnotationPresent(MqttTopic.class)){ + // 如果这个方法有 这个注解 + MqttTopic handleTopic = method.getAnnotation(MqttTopic.class); + if(isMatch(receivedTopic,handleTopic.value())){ + // 并且 这个 topic 匹配成功 + try { + method.invoke(SpringUtils.getBean(clazz),message); + return; + } catch (IllegalAccessException e) { + log.error(e.getMessage()); + log.error("代理炸了"); + } catch (InvocationTargetException e) { + log.error("执行 {} 方法出现错误",handleTopic.value(),e); + } + } + } + } + } + } + + + /** + * mqtt 订阅的主题与我实际的主题是否匹配 + * @param topic 是实际的主题 + * @param pattern 是我订阅的主题 可以是通配符模式 + * @return 是否匹配 + */ + public static boolean isMatch(String topic, String pattern){ + + if((topic==null) || (pattern==null) ){ + return false; + } + + if(topic.equals(pattern)){ + // 完全相等是肯定匹配的 + return true; + } + + if("#".equals(pattern)){ + // # 号代表所有主题 肯定匹配的 + return true; + } + String[] splitTopic = topic.split("/"); + String[] splitPattern = pattern.split("/"); + + boolean match = true; + + // 如果包含 # 则只需要判断 # 前面的 + for (int i = 0; i < splitPattern.length; i++) { + if(!"#".equals(splitPattern[i])){ + // 不是# 号 正常判断 + if(i>=splitTopic.length){ + // 此时长度不相等 不匹配 + match = false; + break; + } + if(!splitTopic[i].equals(splitPattern[i]) && !"+".equals(splitPattern[i])){ + // 不相等 且不等于 + + match = false; + break; + } + } + else { + // 是# 号 肯定匹配的 + break; + } + } + + return match; + } + +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttProperties.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttProperties.java new file mode 100644 index 0000000..fa0c17a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttProperties.java @@ -0,0 +1,79 @@ +package com.yfd.platform.component.mqtt; + + + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * MqttProperties + * + * @date 2022/8/23 + */ +@Data +@Component +public class MqttProperties { + + /** + * 用户名 + */ + @Value("${mqttserver.username}") + private String username; + + /** + * 密码 + */ + @Value("${mqttserver.password}") + private String password; + + /** + * 连接地址 + */ + @Value("${mqttserver.host-url}") + private String hostUrl; + + /** + * 进-客户Id + */ + @Value("${mqttserver.in-client-id}") + private String inClientId; + + /** + * 出-客户Id + */ + @Value("${mqttserver.out-client-id}") + private String outClientId; + + /** + * 客户Id + */ + @Value("${mqttserver.client-id}") + private String clientId; + + /** + * 默认连接话题 + */ + @Value("${mqttserver.default-topic}") + private String defaultTopic; + + /** + * 超时时间 + */ + @Value("${mqttserver.timeout}") + private int timeout; + + /** + * 保持连接数 + */ + @Value("${mqttserver.keepalive}") + private int keepalive; + + /**是否清除session*/ + @Value("${mqttserver.clearSession}") + private boolean clearSession; + + // ...getter and setter + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttService.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttService.java new file mode 100644 index 0000000..76b7581 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttService.java @@ -0,0 +1,19 @@ +package com.yfd.platform.component.mqtt; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Documented +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface MqttService { + + @AliasFor( + annotation = Component.class + ) + String value() default ""; +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttTopic.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttTopic.java new file mode 100644 index 0000000..b5a3301 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttTopic.java @@ -0,0 +1,23 @@ +package com.yfd.platform.component.mqtt; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MqttTopic { + + /** + * 主题名字 + */ + String value() default ""; + + /** + * 是否自动订阅 + */ + boolean autoSubscribe() default true; +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttTopicHandle.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttTopicHandle.java new file mode 100644 index 0000000..43d722c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/MqttTopicHandle.java @@ -0,0 +1,72 @@ +package com.yfd.platform.component.mqtt; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.WeatherLog; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.basedata.service.IWeatherLogService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; +import org.springframework.messaging.Message; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Set; + +/** + * MqttTopicHandle + * + * @date 2022/8/24 + */ +@MqttService +public class MqttTopicHandle { + + public static MqttTopicHandle mqttTopicHandle; + + @Resource + private ISubstationService substationService; + + + + @PostConstruct + public void init() { + mqttTopicHandle = this; + mqttTopicHandle.substationService = this.substationService; + + } + + + public static final Logger log = LoggerFactory.getLogger(MqttTopicHandle.class); + + + // 更新数据库中气象实时数据 +// @MqttTopic("nodes/java/user/alert") +// public void alert(Message message) { +// //更新数据库中气象数据 +// log.info("up=" + message.getPayload()); +// +// } + + @MqttTopic("nodes/java/user/heartbeat") + public void heartbeat(Message message) { + log.info("收到心跳:"+message.toString()); + + } + + + // 这里就不自动订阅该主题了 (参考示例) + @MqttTopic(value = "topic/1/2/down",autoSubscribe = false) + public void down(Message message){ + log.info("down="+message.getPayload()); + } + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/mqtt/client/MqttHeartTimer.java b/riis-system/src/main/java/com/yfd/platform/component/mqtt/client/MqttHeartTimer.java new file mode 100644 index 0000000..6d2305f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/mqtt/client/MqttHeartTimer.java @@ -0,0 +1,77 @@ +package com.yfd.platform.component.mqtt.client; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.component.mqtt.MqttGateway; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * + * @Date: 2023/9/5 16:19 + * @Description: + */ +//@Component +public class MqttHeartTimer { + + @Autowired + private ISubstationService substationService; + @Autowired + private MqttGateway mqttGateway; + + // @Scheduled(cron = "0/30 * * * * ?") + // public void heart_timer1() { + // // 心跳消息由巡视主机定时发送,以通知算法管理平台该巡视主机仍然在线,定时发送时间间隔为30秒 + // List list = substationService.list(); + // Substation substation = list.get(0); + // JSONObject data = new JSONObject(); + // data.putOpt("province_name", substation.getProvinceName()); + // data.putOpt("city_name", substation.getCityName()); + // data.putOpt("volt_level", substation.getVoltLevel()); + // data.putOpt("station_name", substation.getStationName()); + // data.putOpt("section_ip", substation.getStationIp()); + // data.putOpt("node_id", substation.getNodeId()); + // data.putOpt("msg_type", "heart"); + // data.putOpt("time", DateUtil.now()); + // // TODO 上线后开启 + // mqttGateway.sendToMqtt("nodes/java/user/heartbeat", 1,JSONUtil.toJsonStr(data)); + // } + +// @Scheduled(cron = "0/10 * * * * ?") + public void heart_timer1() { + // 发送传感器数据 + JSONArray jsonArray = new JSONArray(); + for (int i = 0; i < 10; i++) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("name", "传感器" + (i + 1)); + jsonObject.putOnce("temperature", RandomUtil.randomInt(30, 50)); + jsonObject.putOnce("humidity", RandomUtil.randomInt(30, 50)); + jsonObject.putOnce("ec", RandomUtil.randomInt(5000, 20000)); + jsonObject.putOnce("ph", RandomUtil.randomInt(1, 12)); + jsonObject.putOnce("npk", RandomUtil.randomInt(200, 2000)); + jsonArray.add(jsonObject); + } + // TODO 上线后开启 + mqttGateway.sendToMqtt("nodes/java/user/sensor1", 1, jsonArray.toString()); + } + +// @Scheduled(cron = "0/10 * * * * ?") + public void heart_timer2() { + // 发送传感器数据 + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("airTemperature", RandomUtil.randomInt(30, 50)); + jsonObject.putOnce("airHumidity", RandomUtil.randomInt(30, 50)); + jsonObject.putOnce("carbonDioxide", RandomUtil.randomInt(1, 20)); + jsonObject.putOnce("lightIntensity", RandomUtil.randomInt(10, 50)); + // TODO 上线后开启 + mqttGateway.sendToMqtt("nodes/java/user/sensor2", 1, jsonObject.toString()); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyChannelInboundHandlerAdapter.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyChannelInboundHandlerAdapter.java new file mode 100644 index 0000000..ec3f58b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyChannelInboundHandlerAdapter.java @@ -0,0 +1,881 @@ +package com.yfd.platform.component.nettyclient; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.AlgorithmServerConfig; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.AlgorithmManufacturerVersion; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.IAlgorithmManufacturerVersionService; +import com.yfd.platform.modules.basedata.service.IAlgorithmModelService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.service.IExaminePlanService; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.patroltask.service.impl.QuartzMultiTaskManage; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.FtpClient; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SpringContextHolder; +import com.yfd.platform.utils.TLSFTPUtils; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.CharsetUtil; +import org.apache.poi.ss.formula.functions.T; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.StringRedisTemplate; + +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * I/O数据读写处理类 + */ +@ChannelHandler.Sharable +public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter { + + private static final Logger logger = LoggerFactory.getLogger(BootNettyChannelInboundHandlerAdapter.class); + /** + * 从服务端收到新的数据时,这个方法会在收到消息时被调用 + */ + // 获取spring bean + IExaminePlanService examinePlanService = SpringContextHolder.getBean(IExaminePlanService.class); + IStationRobotService stationRobotService = SpringContextHolder.getBean(IStationRobotService.class); + ITaskTodoService taskTodoService = SpringContextHolder.getBean(ITaskTodoService.class); + ITaskService taskService = SpringContextHolder.getBean(ITaskService.class); + ISubstationService substationService = SpringContextHolder.getBean(ISubstationService.class); + QuartzMultiTaskManage quartztaskManage = SpringContextHolder.getBean(QuartzMultiTaskManage.class); + HttpServerConfig httpServerConfig = SpringContextHolder.getBean(HttpServerConfig.class); + ParentConfig parentConfig = SpringContextHolder.getBean(ParentConfig.class); + FtpClient ftpClient = SpringContextHolder.getBean(FtpClient.class); + IAlgorithmModelService algorithmModelService = SpringContextHolder.getBean(IAlgorithmModelService.class); + IAlgorithmManufacturerVersionService algorithmManufacturerVersionService = + SpringContextHolder.getBean(IAlgorithmManufacturerVersionService.class); + HeartBeatTask heartBeatTask = SpringContextHolder.getBean(HeartBeatTask.class); + AlgorithmServerConfig algorithmServerConfig = SpringContextHolder.getBean(AlgorithmServerConfig.class); + ISubstationPatroldeviceService substationPatroldeviceService = + SpringContextHolder.getBean(ISubstationPatroldeviceService.class); + IPlatformParentSystemService platformParentSystemService = + SpringContextHolder.getBean(IPlatformParentSystemService.class); + StringRedisTemplate redisTemplate = SpringContextHolder.getBean(StringRedisTemplate.class); + TLSFTPUtils tlsftpUtils = SpringContextHolder.getBean(TLSFTPUtils.class); + HttpRESTfulUtils httpRESTfulUtils = SpringContextHolder.getBean(HttpRESTfulUtils.class); + + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (msg == null || Constant.SYSTEM_MESSAGE_SUCCESS.equals(msg.toString())) { + return; + } + MyMessageProtocol receivedmsg = (MyMessageProtocol) msg; + byte[] xmlcontent = receivedmsg.getXmlcontent(); + if (xmlcontent == null) { + return; + } + String content = new String(xmlcontent); + Map map = MyXmlUtil.xmlToMap(content); + String Type = map.get("Type").toString(); + String Command = map.get("Command").toString(); + String Code = map.get("Code").toString(); + long receiverSerialNo = receivedmsg.getReceiverSerialNo(); + if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(Type) && SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode().equals(Command)) { + String xmlContent = redisTemplate.opsForValue().get(Constant.CLOUD_SEND_CODE + receiverSerialNo); + //上级系统返回注册成功信息 + List> items = (List>) map.get("Items"); + String ReceiveCode = map.get("ReceiveCode").toString(); + logger.info("-------------ReceiveCode------------------" + ReceiveCode); + if (StrUtil.isBlank(xmlContent)) { + return; + } + logger.info(xmlContent); + if (xmlContent.startsWith("\"") && xmlContent.endsWith("\"")) { + xmlContent = xmlContent.substring(1, xmlContent.length() - 1); + } + xmlContent = xmlContent.replace("\\n", "").replace("\\", ""); + Map xmlMap = MyXmlUtil.xmlToMap(xmlContent); + // 发送的指令 + String responseType = xmlMap.get("Type").toString(); + List> xmlItems = (List>) xmlMap.get("Items"); + + if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(responseType)) { + // 上级心跳响应 + parentHeartbeatResponse(items, xmlMap, ReceiveCode, Code); + } + + if (SystemCode.TYPE_ALGORITHM_PARAM_CODE.getCode().equals(responseType) && xmlItems.size() > 0) { + // 获取当前该区域某站点的运行算法参数 + getRunAlgorithmParams(xmlItems, items); + return; + } + if (SystemCode.TYPE_ALGORITHM_VERSION_CODE.getCode().equals(responseType)) { + getAlgorithmVersion(items); + return; + } + if (SystemCode.TYPE_ALGORITHM_UPDATE_CODE.getCode().equals(responseType) && true) { + // 算法版本更新 + updateAlgorithmVersion(xmlItems, items); + return; + } + } else if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(Type) && SystemCode.COMMAND_RESPONSE_CODE.getCode().equals(Command)) { + //上级系统返回心跳保成功 + logger.info("上级系统返回心跳保持成功!"); + } else if (SystemCode.TYPE_TASK_CONTROL_CODE.getCode().equals(Type)) { + // 上级系统发起任务操作 1:启用 2:暂停 3:恢复 4:停止 + // 如果Command为1,Code为任务id,否则为任务执行id + logger.info(content); + taskControl(ctx, receivedmsg, map, Type, Command, Code); + + } else if (SystemCode.TYPE_TASK_ISSUED_CODE.getCode().equals(Type) && SystemCode.COMMAND_TASK_CONFIG_CODE.getCode().equals(Command)) { + logger.info(content); + // 上级发送任务 + loadTaskDistribution(ctx, receivedmsg, map, Type, Command, Code); + } else if (SystemCode.TYPE_EXAMINE_ISSUED_CODE.getCode().equals(Type) && SystemCode.COMMAND_EXAMINE_CONFIG_CODE.getCode().equals(Command)) { + // 上级发送检修区域 + examineAreaCommand(ctx, receivedmsg, map, Code); + } else if (SystemCode.TYPE_MODEL_SYNC_CODE.getCode().equals(Type)) { + //上级发送模型同步 + modelSyncCommand(ctx, msg, map, Command); + } else if (SystemCode.TYPE_TASK_COUNT_CODE.getCode().equals(Type)) { + logger.info("===========================" + content); + // 巡视结果统计查询 + patrolResultCount(ctx, msg, map, Code, Command); + } else if (SystemCode.TYPE_SOLIDITY_INDEX_CODE.getCode().equals(Type)) { + //可靠性指标统计查询 + getSolidityIndex(ctx, msg, map, Code, Command); + } else { + // 其他上级控制方法 + otherControlMethod(ctx, receivedmsg, map, Type, Command, Code); + } + + BootNettyClientChannel bootNettyClientChannel = BootNettyClientChannelCache.get(ctx.channel().id().toString()); + if (bootNettyClientChannel != null) { + bootNettyClientChannel.setLast_data(msg.toString()); + } + } + + /*********************************** + * 用途说明: 上级心跳响应 + * 参数说明 items 上级上级心跳参数 + * 参数说明 xmlMap 发送参数 + * 参数说明 ReceiveCode 接收编号 + * 参数说明 Code 编号 + * 返回值说明: void + ***********************************/ + private void parentHeartbeatResponse(List> items, Map xmlMap, + String ReceiveCode, String Code) { + String responseCommand = ObjectUtil.isEmpty(xmlMap.get("Command")) ? "" : + xmlMap.get("Command").toString(); + if ("1".equals(responseCommand)) { + for (Map item : items) { + for (String key : item.keySet()) { + if ("heart_beat_interval".equals(key)) { + parentConfig.setHeartBeatInterval(item.get(key)); + } + if ("patroldevice_run_interval".equals(key)) { + parentConfig.setPatroldeviceRunInterval(item.get(key)); + } + if ("nest_run_interval".equals(key)) { + parentConfig.setNestRunInterval(item.get(key)); + } + if ("weather_interval".equals(key)) { + parentConfig.setWeatherInterval(item.get(key)); + } + } + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(PlatformParentSystem::getChildCode, ReceiveCode).set(PlatformParentSystem::getIsFlag, "1"); + platformParentSystemService.update(updateWrapper); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PlatformParentSystem::getChildCode, ReceiveCode); + List list = platformParentSystemService.list(queryWrapper); + if (list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + int time = Integer.parseInt(parentConfig.getHeartBeatInterval()); + ParentConfig parentConfig = new ParentConfig(); + parentConfig.setParentId(platformParentSystem.getParentCode()); + parentConfig.setServerId(platformParentSystem.getChildCode()); + parentConfig.setTcpPort(platformParentSystem.getServerPort()); + parentConfig.setParentIp(platformParentSystem.getServerIp()); + logger.info("time" + time + "==============================ParentConfig========================" + parentConfig.toString()); + heartBeatTask.startCron("HeartBeatTask", new HeartBeatJob(parentConfig), time * 1000); + logger.info("上级系统返回注册成功信息!" + Code); + } + } + } + + /*********************************** + * 用途说明: 获取当前该区域某站点的运行算法参数 + * 参数说明 xmlItems 控制缓存 + * 参数说明 items 上级控制数据 + * 返回值说明: void + ***********************************/ + private void getRunAlgorithmParams(List> xmlItems, List> items) { + Map responseMap = xmlItems.get(0); + String type = responseMap.get("type"); + for (Map item : items) { + AlgorithmModel algorithmModel = BeanUtil.copyProperties(item, AlgorithmModel.class); + algorithmModel.setLastmodifier("admin"); + algorithmModel.setAlgorithmmanufacturer(item.get("algorithm_manufacturer")); + algorithmModel.setRunstate(item.get("run_state")); + algorithmModel.setMidversionnum(NumberUtil.parseInt(item.get("mid_version_num"))); + algorithmModel.setLastmodifydate(LocalDateTime.now()); + algorithmModel.setModelType(type); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlgorithmModel::getStationId, algorithmModel.getStationId()).eq(AlgorithmModel::getModelType, algorithmModel.getModelType()); + int count = algorithmModelService.count(queryWrapper); + String dateStr = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"); + algorithmModel.setModelCode(dateStr); + algorithmModel.setModelName(dateStr); + if (count > 0) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AlgorithmModel::getStationId, algorithmModel.getStationId()).eq(AlgorithmModel::getModelType, algorithmModel.getModelType()); + algorithmModelService.update(algorithmModel, updateWrapper); + } else { + algorithmModel.setIsnew("1"); + algorithmModelService.save(algorithmModel); + } + } + } + + /*********************************** + * 用途说明: 算法版本获取 + * 参数说明 xmlItems 控制缓存 + * 参数说明 items 上级控制数据 + * 返回值说明: void + ***********************************/ + private void getAlgorithmVersion(List> items) { + for (Map item : items) { + AlgorithmManufacturerVersion algorithmModel = BeanUtil.copyProperties(item, + AlgorithmManufacturerVersion.class); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlgorithmManufacturerVersion::getAlgorithmManufacturer, + algorithmModel.getAlgorithmManufacturer()).eq(AlgorithmManufacturerVersion::getVersion, + algorithmModel.getVersion()); + int count = algorithmManufacturerVersionService.count(queryWrapper); + if (count <= 0) { + algorithmManufacturerVersionService.save(algorithmModel); + } + + } + } + + /*********************************** + * 用途说明: 算法版本更新 + * 参数说明 xmlItems 控制缓存 + * 参数说明 items 上级控制数据 + * 返回值说明: void + ***********************************/ + private void updateAlgorithmVersion(List> xmlItems, List> items) { + Map responseMap = xmlItems.get(0); + String type = responseMap.get("type"); + for (Map item : items) { + AlgorithmModel algorithmModel = BeanUtil.copyProperties(item, AlgorithmModel.class); + algorithmModel.setStationId(responseMap.get("station_id")); + algorithmModel.setSectionId(responseMap.get("section_id")); + algorithmModel.setModelType(type); + String algorithm_path = item.get("algorithm_path"); + String path = StrUtil.subAfter(algorithm_path, "/", true); + algorithmModel.setModelFile(path); + logger.info("=======item========" + item.toString()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlgorithmModel::getStationId, algorithmModel.getStationId()).eq(AlgorithmModel::getModelType, algorithmModel.getModelType()); + List list = algorithmModelService.list(queryWrapper); + List list1 = + platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + if (list.size() > 0 && list1.size() > 0) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AlgorithmModel::getStationId, algorithmModel.getStationId()).eq(AlgorithmModel::getModelType, algorithmModel.getModelType()); + AlgorithmModel algorithmModel1 = list.get(0); + PlatformParentSystem platformParentSystem = list1.get(0); + new Thread(() -> { + try { + if (tlsftpUtils.isConnected()) { + tlsftpUtils.disconnect(); + } + tlsftpUtils.connect(platformParentSystem.getFtpIp() + , platformParentSystem.getFtpUser(), + platformParentSystem.getFtpPassword(), + NumberUtil.parseInt(platformParentSystem.getFtpPort())); + logger.info("==============algorithmModel.getModelFile==============" + algorithmModel + .getModelFile()); + tlsftpUtils.download(httpServerConfig.getModelPath + (), algorithmModel.getModelFile()); + tlsftpUtils.disconnect(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("requestHostIp", httpServerConfig.getPatrolIp()); + jsonObject.putOnce("requestHostPort", httpServerConfig.getPatrolPort()); + jsonObject.putOnce("requestId", algorithmModel1.getId()); + jsonObject.putOnce("type", type); + String ftpUrl = + "ftp://" + algorithmServerConfig.getFtpUsername() + ":" + algorithmServerConfig.getFtpPassword() + "@" + algorithmServerConfig.getServerIp() + ":" + algorithmServerConfig.getFtpPort() + "/" + algorithmModel.getModelFile(); + jsonObject.putOnce("algorithmPath", ftpUrl); + logger.info(jsonObject.toString()); + com.alibaba.fastjson.JSONObject jsonObject1 = httpRESTfulUtils.sendHttpPost("json", + httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort13(), "", + "algorithmUpdate", jsonObject, null); + if (SystemCode.SUCCESS_STATUS_CODE.getCode().equals(jsonObject1.getString("code"))) { + algorithmModelService.update(algorithmModel, updateWrapper); + } + } catch (Exception e) { + tlsftpUtils.disconnect(); + logger.error(e.getMessage()); + throw new RuntimeException("更新算法报错", e); + } + }).start(); + + } + } + } + + /*********************************** + * 用途说明: 任务控制 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 receivedmsg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Type 上级控制类型 + * 参数说明 Command 上级控制命令 + * 参数说明 Code 编号 + * 返回值说明: void + ***********************************/ + private void taskControl(ChannelHandlerContext ctx, MyMessageProtocol receivedmsg, + Map map, String Type, String Command, String Code) throws InterruptedException { + JSONArray taskArray = new JSONArray(); + if (SystemCode.COMMAND_TASK_START_CODE.getCode().equals(Command)) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Task::getTaskCode, Code).set(Task::getIsenable, "1"); + taskService.update(updateWrapper); + // 查询任务 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Task::getTaskCode, Code); + Task task = taskService.getOne(queryWrapper); + String robotCode = task.getRobotCode(); + if (StrUtil.isNotBlank(robotCode)) { + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, robotCode)); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), + httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), Type, Command, + Code, null); + + } + } else { + // 控制 + List list = taskTodoService.list(new LambdaQueryWrapper().eq(TaskTodo::getTaskId, + task.getTaskId()).select(TaskTodo::getTaskTodoId)); + for (TaskTodo taskTodo : list) { + JSONObject taskData = new JSONObject(); + taskData.putOnce("task_patrolled_id", taskTodo.getTaskTodoId()); + if (taskArray.size() <= 0) { + //摄像头命令 暂停 + taskTodoService.doCurrentTask(taskTodo.getTaskTodoId(), SystemCode.TASK_RUN_CODE.getCode()); + } + taskArray.add(taskData); + } + } + + } + + if (SystemCode.COMMAND_TASK_PAUSE_CODE.getCode().equals(Command)) { + JSONObject taskData = new JSONObject(); + taskData.putOnce("task_patrolled_id", Code); + taskArray.add(taskData); + TaskTodo taskTodo = taskTodoService.getById(Code); + if (StrUtil.isNotBlank(taskTodo.getRobotCode())) { + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, taskTodo.getRobotCode())); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), + httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), Type, Command, + Code, null); + } + } else { + //摄像头命令 暂停 + taskTodoService.doCurrentTask(taskTodo.getTaskTodoId(), SystemCode.TASK_PAUSE_CODE.getCode()); + } + } + + if (SystemCode.COMMAND_TASK_RESUME_CODE.getCode().equals(Command)) { + JSONObject taskData = new JSONObject(); + taskData.putOnce("task_patrolled_id", Code); + taskArray.add(taskData); + TaskTodo taskTodo = taskTodoService.getById(Code); + if (StrUtil.isNotBlank(taskTodo.getRobotCode())) { + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, taskTodo.getRobotCode())); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), + httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), Type, Command, + Code, null); + } + } else { + //摄像头命令 恢复 + taskTodoService.doCurrentTask(taskTodo.getTaskTodoId(), SystemCode.TASK_RESUME_CODE.getCode()); + } + } + + if (SystemCode.COMMAND_TASK_STOP_CODE.getCode().equals(Command)) { + JSONObject taskData = new JSONObject(); + taskData.putOnce("task_patrolled_id", Code); + taskArray.add(taskData); + TaskTodo taskTodo = taskTodoService.getById(Code); + if (StrUtil.isNotBlank(taskTodo.getRobotCode())) { + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, taskTodo.getRobotCode())); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), + httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), Type, + Command, Code, null); + } + } else { + //摄像头命令 终止 + taskTodoService.doCurrentTask(taskTodo.getTaskTodoId(), SystemCode.TASK_STOP_CODE.getCode()); + } + } + // 响应给上级系统 + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), taskArray.toString()); + sendResponseData(ctx, receivedmsg, reponsexml); + + } + + /*********************************** + * 用途说明: 任务下发 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 receivedmsg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Type 上级控制类型 + * 参数说明 Command 上级控制命令 + * 参数说明 Code 编号 + * 返回值说明: void + ***********************************/ + private void loadTaskDistribution(ChannelHandlerContext ctx, MyMessageProtocol receivedmsg, + Map map, String Type, String Command, String Code) throws ParseException { + // 上级发送任务 + List> items = (List>) map.get("Items"); + String robotCode = ""; + for (Map item : items) { + Task task = BeanUtil.toBean(item, Task.class); + Substation substation = + substationService.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, + Code)); + task.setDatastatus("2"); + task.setTaskId(IdUtil.fastSimpleUUID()); + robotCode = task.getRobotCode(); + task.setStationCode(Code); + String fixedStartTime = task.getFixedStartTime(); + if (StrUtil.isNotBlank(fixedStartTime)) { + task.setTaskTodoType("2"); + } + task.setTaskType("1"); + task.setStationName(substation.getStationName()); + task.setLastmodifier("上级系统"); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + task.setLastmodifydate(timestamp); + task.setCreateTime(new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(timestamp)); + taskService.save(task); + if (StrUtil.isBlank(robotCode)) { + //摄像头命令 + String taskTodoType = task.getTaskTodoType(); + if ("1".equals(taskTodoType)) { + //立即执行 + TaskTodo taskTodo = taskService.createRunNowTask(task); + //立即执行当前任务 + quartztaskManage.addJob(taskTodo); + } else {//定期或者周期执行 + taskService.createTodoTaskList(task); + } + } + } + if (StrUtil.isNotBlank(robotCode)) { + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, robotCode)); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + // 发送任务到机器人 + JSONArray jsonArray = JSONUtil.parseArray(items); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), + httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), Type, Command, + Code, jsonArray.toString()); + } + } + + // 响应给上级系统 + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, receivedmsg, reponsexml); + } + + /*********************************** + * 用途说明: 上级发送检修区域 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 receivedmsg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Code 编号 + * 返回值说明: void + ***********************************/ + private void examineAreaCommand(ChannelHandlerContext ctx, MyMessageProtocol receivedmsg, Map map + , String Code) { + List> items = (List>) map.get("Items"); + for (Map item : items) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // Code为变电站Code + queryWrapper.eq(Substation::getStationCode, Code); + Substation substation = substationService.getOne(queryWrapper); + ExaminePlan examinePlan = BeanUtil.toBean(item, ExaminePlan.class); + examinePlan.setStationId(substation.getStationId()); + examinePlan.setStationCode(substation.getStationCode()); + examinePlan.setStationName(substation.getStationName()); + examinePlan.setConfigName("检修" + examinePlan.getConfigCode()); + examinePlan.setDatastatus("1"); + examinePlan.setLastmodifier("上级系统"); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + examinePlan.setLastmodifydate(timestamp); + examinePlan.setCreator("上级系统"); + examinePlan.setCreateTime(new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(timestamp)); + examinePlanService.save(examinePlan); + } + // 响应给上级系统 + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, receivedmsg, reponsexml); + } + + /*********************************** + * 用途说明: 上级发送模型同步 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 msg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Command 控制编码 + * 返回值说明: void + ***********************************/ + private void modelSyncCommand(ChannelHandlerContext ctx, Object msg, Map map, String Command) throws Exception { + String filePath = ""; + JSONObject taskData = new JSONObject(); + // 上级发送检修区域 + filePath = stationRobotService.reportParentById("", Command); + String filepath = filePath; + String path = "/home/ftpsuser/" + StrUtil.subBefore(filepath, "/", true); + if (SystemCode.COMMAND_MODEL_HOST_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "巡视主机模型"); + taskData.putOpt(SystemCode.COMMAND_HOST_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_ROBOT_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "机器人模型"); + taskData.putOpt(SystemCode.COMMAND_ROBOT_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_CAMERA_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "摄像机模型"); + taskData.putOpt(SystemCode.COMMAND_VIDEO_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_DEVICE_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "点位模型"); + taskData.putOpt(SystemCode.COMMAND_DEVICE_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_DRONE_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "无人机模型"); + taskData.putOpt(SystemCode.COMMAND_DRONE_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_VOICE_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "声纹模型"); + taskData.putOpt(SystemCode.COMMAND_VOICE_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_TASK_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "任务文件"); + taskData.putOpt(SystemCode.COMMAND_TASK_FILE_PATH.getCode(), filepath); + } else if (SystemCode.COMMAND_MODEL_EXAMINE_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "检修区域配置文件"); + taskData.putOpt("overhaularea_file_path", filepath); + } else if (SystemCode.COMMAND_MODEL_MAP_CODE.getCode().equals(Command)) { + logger.info("上级系统向区域巡视主机发起同步模型请求!<{}>", "地图文件"); + taskData.putOpt(SystemCode.COMMAND_MAP_FILE_PATH.getCode(), filepath); + } + List list = + platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + if (list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + if (tlsftpUtils.isConnected()) { + tlsftpUtils.disconnect(); + } + tlsftpUtils.connect(platformParentSystem.getFtpIp(), platformParentSystem.getFtpUser(), + platformParentSystem.getFtpPassword(), NumberUtil.parseInt(platformParentSystem.getFtpPort())); + logger.info("====platformParentSystem===" + platformParentSystem.toString()); + // String path = + tlsftpUtils.upload(path, new File(httpServerConfig.getModelPath() + filePath)); + tlsftpUtils.disconnect(); + } + logger.info("==================taskData====================" + taskData.toString()); + this.responseMessage(ctx, map, msg, SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), + SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode(), taskData.toString()); + } + + /*********************************** + * 用途说明: 巡视结果统计查询 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 msg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Code 编号 + * 参数说明 Command 控制编码 + * 返回值说明: void + ***********************************/ + private void patrolResultCount(ChannelHandlerContext ctx, Object msg, Map map, String Code, + String Command) { + List> items = (List>) map.get("Items"); + for (Map jsonObject : items) { + String beginTime = jsonObject.get(SystemCode.ITEM_BEGIN_TIME.getCode()); + String endTime = jsonObject.get(SystemCode.ITEM_END_TIME.getCode()); + List> taskDataStat = taskTodoService.getTaskDataStat(Code, "1", beginTime, endTime); + long count1 = 0; + long sum1 = 0; + JSONObject jsonObject1 = new JSONObject(); + if (SystemCode.COMMAND_CLOSE_LOOP_CODE.getCode().equals(Command)) { + for (Map stringObjectMap : taskDataStat) { + if (ObjectUtil.isNotEmpty(stringObjectMap.get("count2"))) { + count1 += Integer.parseInt(stringObjectMap.get("count2").toString()); + } + if (ObjectUtil.isNotEmpty(stringObjectMap.get("sum2"))) { + sum1 += Integer.parseInt(stringObjectMap.get("sum2").toString()); + } + } + } else if (SystemCode.COMMAND_ALARM_COMPLETE_CODE.getCode().equals(Command)) { + for (Map stringObjectMap : taskDataStat) { + if (ObjectUtil.isNotEmpty(stringObjectMap.get("count5"))) { + count1 += Integer.parseInt(stringObjectMap.get("count5").toString()); + } + if (ObjectUtil.isNotEmpty(stringObjectMap.get("sum5"))) { + sum1 += Integer.parseInt(stringObjectMap.get("sum5").toString()); + } + } + } else if (SystemCode.COMMAND_ALARM_ACCURATE_CODE.getCode().equals(Command)) { + for (Map stringObjectMap : taskDataStat) { + if (ObjectUtil.isNotEmpty(stringObjectMap.get("count3"))) { + count1 += Integer.parseInt(stringObjectMap.get("count3").toString()); + } + if (ObjectUtil.isNotEmpty(stringObjectMap.get("sum3"))) { + sum1 += Integer.parseInt(stringObjectMap.get("sum3").toString()); + } + } + } else if (SystemCode.COMMAND_PERSON_EXAMINE_CODE.getCode().equals(Command)) { + for (Map stringObjectMap : taskDataStat) { + if (ObjectUtil.isNotEmpty(stringObjectMap.get("count4"))) { + count1 += Integer.parseInt(stringObjectMap.get("count4").toString()); + } + if (ObjectUtil.isNotEmpty(stringObjectMap.get("sum4"))) { + sum1 += Integer.parseInt(stringObjectMap.get("sum4").toString()); + } + } + } else if (SystemCode.COMMAND_MISS_CHECK_CODE.getCode().equals(Command)) { + for (Map stringObjectMap : taskDataStat) { + if (ObjectUtil.isNotEmpty(stringObjectMap.get("count1"))) { + count1 += Integer.parseInt(stringObjectMap.get("count1").toString()); + } + if (ObjectUtil.isNotEmpty(stringObjectMap.get("sum1"))) { + sum1 += Integer.parseInt(stringObjectMap.get("sum1").toString()); + } + } + + } + jsonObject1.putOnce(SystemCode.ITEM_TOTAL_NUM.getCode(), sum1); + jsonObject1.putOnce(SystemCode.ITEM_VALID_NUM.getCode(), count1); + String num; + if (sum1 == 0) { + num = "0"; + } else { + num = String.format("%.3f", ((float) sum1 * 100 / count1)); + } + jsonObject1.putOnce(SystemCode.ITEM_PERCENT.getCode(), num); + logger.info("============responseMessage===============" + jsonObject1.toString()); + this.responseMessage(ctx, map, msg, SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), + SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode(), jsonObject1.toString()); + } + } + + /*********************************** + * 用途说明: 可靠性指标统计查询 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 msg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Code 编号 + * 参数说明 Command 控制编码 + * 返回值说明: void + ***********************************/ + private void getSolidityIndex(ChannelHandlerContext ctx, Object msg, Map map, String Code, + String Command) { + List> items = (List>) map.get("Items"); + for (Map jsonObject : items) { + String beginTime = jsonObject.get(SystemCode.ITEM_BEGIN_TIME.getCode()); + String endTime = jsonObject.get(SystemCode.ITEM_END_TIME.getCode()); + List> taskDataStat = taskTodoService.getTaskDataStat(Code, "1", beginTime, endTime); + long count1 = 0; + long sum1 = 0; + JSONObject jsonObject1 = new JSONObject(); + if (SystemCode.COMMAND_ALARM_EXACT_CODE.getCode().equals(Command)) { + // 巡视告警准确率 + for (Map stringObjectMap : taskDataStat) { + if (ObjectUtil.isNotEmpty(stringObjectMap.get("count3"))) { + count1 += Integer.parseInt(stringObjectMap.get("count3").toString()); + } + if (ObjectUtil.isNotEmpty(stringObjectMap.get("sum3"))) { + sum1 += Integer.parseInt(stringObjectMap.get("sum3").toString()); + } + } + } else if (SystemCode.COMMAND_ALGORITHM_EXACT_CODE.getCode().equals(Command)) { + // 算法标签对应准确率 + jsonObject1.putOpt("tag_type", "sly_bjbmyw"); + } + jsonObject1.putOnce(SystemCode.ITEM_TOTAL_NUM.getCode(), sum1); + jsonObject1.putOnce(SystemCode.ITEM_VALID_NUM.getCode(), count1); + String num; + if (sum1 == 0) { + num = "0"; + } else { + num = String.format("%.3f", ((float) sum1 * 100 / count1)); + } + jsonObject1.putOnce(SystemCode.ITEM_PERCENT.getCode(), num); + this.responseMessage(ctx, map, msg, SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), + SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode(), jsonObject1.toString()); + } + } + + /*********************************** + * 用途说明: 其他上级控制指令 + * 参数说明 ctx 上级通道处理程序 + * 参数说明 receivedmsg 上级发送数据对象 + * 参数说明 map 提取后的上级参数 + * 参数说明 Type 上级控制类型 + * 参数说明 Command 上级控制命令 + * 参数说明 Code 编号 + * 返回值说明: void + ***********************************/ + private void otherControlMethod(ChannelHandlerContext ctx, MyMessageProtocol receivedmsg, Map map + , String Type, String Command, String Code) { + logger.info("上级系统发送命令!type{},command{}", Type, Command); + stationRobotService.sendCommand("", httpServerConfig.getPatrolServerid(), "", Type, Command, Code, null); + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, receivedmsg, reponsexml); + } + + /** + * 从服务端收到新的数据、读取完成时调用 + */ + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws IOException { + logger.info("channelReadComplete"); + ctx.flush(); + } + + /** + * 当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时 + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException { + logger.info("exceptionCaught"); + logger.error(cause.getMessage()); + ctx.close();//抛出异常,断开与客户端的连接 + } + + /** + * 客户端与服务端第一次建立连接时 执行 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = inSocket.getAddress().getHostAddress(); + logger.info(clientIp); + } + + /** + * 客户端与服务端 断连时 执行 + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) { + InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = inSocket.getAddress().getHostAddress(); + platformParentSystemService.update(new LambdaUpdateWrapper().eq(PlatformParentSystem::getServerIp, clientIp).set(PlatformParentSystem::getIsFlag, "0")); + List list = + platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getServerIp, clientIp)); +// heartBeatTask.stop("HeartBeatTask"); + if (list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + BootNettyClientChannel bootNettyClientChannel = + BootNettyClientChannelCache.channelMapCache.get(platformParentSystem.getChildCode()); + if (bootNettyClientChannel != null) { + bootNettyClientChannel.getChannel().close(); + BootNettyClientChannelCache.channelMapCache.remove(platformParentSystem.getChildCode()); + } + } + logger.error("上级系统与区域巡视主机服务器断开连接了!" + clientIp); + } + + /********************************** + * 用途说明: 发送响应数据 + * 参数说明 ctx + * 参数说明 msg + * 参数说明 reponsexml + * 返回值说明: void + ***********************************/ + public void sendResponseData(ChannelHandlerContext ctx, MyMessageProtocol msg, String reponsexml) { + WebSocketServer.sendInfo("robotinfo", reponsexml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getResponseSessionID()); + messageProtocol.setReceiverSerialNo(msg.getSenderSerialNo()); + messageProtocol.setSessionflag(1); + messageProtocol.setXmllen(reponsexml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(reponsexml.getBytes(CharsetUtil.UTF_8)); + ctx.write(messageProtocol); + ctx.flush(); + } + + public void responseMessage(ChannelHandlerContext ctx, Map map, Object msg, String Type, + String Command, String Items) { + MyMessageProtocol receivedmsg = (MyMessageProtocol) msg; + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), Type, + Command, SystemCode.SUCCESS_STATUS_CODE.getCode(), Items); + boolean open = ctx.channel().isOpen(); + logger.info("===============open==============" + open); + WebSocketServer.sendInfo("robotinfo", reponsexml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getResponseSessionID()); + messageProtocol.setReceiverSerialNo(receivedmsg.getSenderSerialNo()); + messageProtocol.setSessionflag(1); + messageProtocol.setXmllen(reponsexml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(reponsexml.getBytes(CharsetUtil.UTF_8)); + ctx.write(messageProtocol); + ctx.flush(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyChannelInitializer.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyChannelInitializer.java new file mode 100644 index 0000000..3b7494e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyChannelInitializer.java @@ -0,0 +1,34 @@ +package com.yfd.platform.component.nettyclient; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; + +/** + * 通道初始化 + + */ +@ChannelHandler.Sharable +public class BootNettyChannelInitializer extends ChannelInitializer { + private static final int MAX_FRAME_LENGTH = 1024 * 1024; //最大长度 + private static final int LENGTH_FIELD_LENGTH = 4; //长度字段所占的字节数 + private static final int LENGTH_FIELD_OFFSET = 19; //长度偏移 + private static final int LENGTH_ADJUSTMENT = 0; + private static final int INITIAL_BYTES_TO_STRIP = 0; + private static final byte[] DELIMITER = {(byte) 0xEB, (byte) 0x90}; + @Override + protected void initChannel(Channel ch) throws Exception { + + ch.pipeline().addLast("encoder", new MyMessageEncoder()); + ch.pipeline().addLast(new MyDelimiterBasedFrameDecoder(MAX_FRAME_LENGTH, + Unpooled.wrappedBuffer(DELIMITER))); +// ch.pipeline().addLast("decoder", new MyMessageDecoder()); + /** + * 自定义ChannelInboundHandlerAdapter + */ + ch.pipeline().addLast(new BootNettyChannelInboundHandlerAdapter()); + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClient.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClient.java new file mode 100644 index 0000000..4fa1166 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClient.java @@ -0,0 +1,109 @@ +package com.yfd.platform.component.nettyclient; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.utils.SpringContextHolder; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +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.NioSocketChannel; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.StringRedisTemplate; + +import java.util.List; + +import static com.yfd.platform.component.nettyclient.BootSessionCache.requestSessionID; + +/** + * + + */ +public class BootNettyClient { + private static final Logger logger = LoggerFactory.getLogger(BootNettyClient.class); + + + public void connect(String ServerIP,int Serverport, ParentConfig parentConfig) throws InterruptedException { + // 获取spring bean + IPlatformParentSystemService platformParentSystemService = SpringContextHolder.getBean(IPlatformParentSystemService.class); + HeartBeatTask heartBeatTask = SpringContextHolder.getBean(HeartBeatTask.class); + /** + * 客户端的NIO线程组 + * + */ + EventLoopGroup group = new NioEventLoopGroup(); + + try { + /** + * Bootstrap 是一个启动NIO服务的辅助启动类 客户端的 + */ + Bootstrap bootstrap = new Bootstrap(); + bootstrap = bootstrap.group(group); + bootstrap = bootstrap.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true); + /** + * 设置 I/O处理类,主要用于网络I/O事件,记录日志,编码、解码消息 + */ + bootstrap = bootstrap.handler(new BootNettyChannelInitializer()); + /** + * 连接服务端 + */ + logger.info("ServerIP"+ServerIP+"Serverport"+Serverport); + ChannelFuture future = bootstrap.connect(ServerIP, Serverport).sync(); + if(future.isSuccess()) { + logger.info("成功进入方法"); + Channel channel = future.channel(); + String id = future.channel().id().toString(); + BootNettyClientChannel bootNettyClientChannel = new BootNettyClientChannel(); + bootNettyClientChannel.setChannel(channel); + bootNettyClientChannel.setCode(parentConfig.getServerId()); +// BootNettyClientChannelCache.remove(parentConfig.getServerId());//清除缓存 + BootNettyClientChannelCache.save(parentConfig.getServerId(), bootNettyClientChannel); + //to do 发起一次注册请求 + registerToServer(bootNettyClientChannel,parentConfig); + future.channel().closeFuture().sync(); + } + } catch (Exception e) { + logger.error("上级系统{}:{},区域巡视主机无法连接到服务器{}!",ServerIP,Serverport,parentConfig.getParentId()); + } finally { + /** + * 退出,释放资源 + */ + group.shutdownGracefully().sync(); + } + + } + + private void registerToServer(BootNettyClientChannel bootNettyClientChannel, ParentConfig parentConfig){ + StringRedisTemplate redisTemplate = SpringContextHolder.getBean(StringRedisTemplate.class); + //getXml(String SendCode, String ReceiveCode, String Type, String Command, String Code, List< Map > items) + String xml=MyXmlUtil.getXml(parentConfig.getServerId(),parentConfig.getParentId(), SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(),SystemCode.COMMAND_REGISTER_CODE.getCode(),"",null); + logger.info("注册xml"+xml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + long requestSessionID = BootSessionCache.getRequestSessionID(); + messageProtocol.setSenderSerialNo(requestSessionID); + redisTemplate.opsForValue().set(Constant.CLOUD_SEND_CODE + requestSessionID, xml); + messageProtocol.setReceiverSerialNo(0L); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if(bootNettyClientChannel != null && bootNettyClientChannel.getChannel().isOpen()){ + logger.info("通道是通的"); + bootNettyClientChannel.getChannel().writeAndFlush(messageProtocol); + }else{ + logger.info("通道是关的"); + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientChannel.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientChannel.java new file mode 100644 index 0000000..ada5520 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientChannel.java @@ -0,0 +1,41 @@ +package com.yfd.platform.component.nettyclient; + +import io.netty.channel.Channel; + +/** + * 建立channel保存多客户端BootNettyClientChannel + */ +public class BootNettyClientChannel { + + // 连接客户端唯一的code + private String code; + + // 客户端最新发送的消息内容 + private String last_data; + + private transient volatile Channel channel; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Channel getChannel() { + return channel; + } + + public void setChannel(Channel channel) { + this.channel = channel; + } + + public String getLast_data() { + return last_data; + } + + public void setLast_data(String last_data) { + this.last_data = last_data; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientChannelCache.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientChannelCache.java new file mode 100644 index 0000000..11e145a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientChannelCache.java @@ -0,0 +1,32 @@ +package com.yfd.platform.component.nettyclient; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * TCP通道缓存 + */ +public class BootNettyClientChannelCache { + + public static volatile Map channelMapCache = new ConcurrentHashMap(); + + public static void add(String code, BootNettyClientChannel channel){ + channelMapCache.put(code,channel); + } + + public static BootNettyClientChannel get(String code){ + return channelMapCache.get(code); + } + + public static void remove(String code){ + channelMapCache.remove(code); + } + + public static void save(String code, BootNettyClientChannel channel) { + if(channelMapCache.get(code) == null) { + add(code,channel); + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientThread.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientThread.java new file mode 100644 index 0000000..e2eda44 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyClientThread.java @@ -0,0 +1,28 @@ +package com.yfd.platform.component.nettyclient; + +import com.yfd.platform.config.ParentConfig; + +/** + * + * netty 客户端 + * 启动线程 + */ +public class BootNettyClientThread extends Thread { + private final String ServerIP; + private final int Serverport; + private final ParentConfig parentConfig; + public BootNettyClientThread(String ServerIP, int Serverport, ParentConfig parentConfig){ + this.ServerIP = ServerIP; + this.Serverport = Serverport; + this.parentConfig = parentConfig; + } + + @Override + public void run() { + try { + new BootNettyClient().connect(ServerIP, Serverport,parentConfig); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyHeartTimer.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyHeartTimer.java new file mode 100644 index 0000000..b0fc7e8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootNettyHeartTimer.java @@ -0,0 +1,93 @@ +package com.yfd.platform.component.nettyclient; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.mapper.WeatherLogMapper; +import com.yfd.platform.modules.basedata.service.IAlgorithmModelService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 心跳保持器 + */ +@Component +public class BootNettyHeartTimer { + + @Resource + private WeatherLogMapper weatherLogMapper; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private ISubstationService substationService; + @Resource + private IAlgorithmModelService algorithmModelService; + + @Scheduled(cron = "0 0 1 * * ?") + public void timeTask() { + // 删除一天以前的温度数据 + weatherLogMapper.deleteWeatherLog(); + } + + + /********************************** + * 用途说明: 发送请求获取系统自检信息/资源上报 + * 参数说明 + * 返回值说明: void + ***********************************/ + // @Scheduled(cron = "0/30 * * * * ?") + public void algorithmResource() throws IOException { + // 调用分析主机获取设备资源信息 + String api1 = "algorithmResource"; + Map param1 = new HashMap<>(); + JSONObject jsonObject = httpRESTfulUtils.sendHttpPost("json", httpServerConfig.getAnalyseIp(), + httpServerConfig.getAnalysePort13(), + "", api1, param1, null); + if (SystemCode.SUCCESS_STATUS_CODE.getCode().equals(jsonObject.getString("code"))) { + JSONObject data = jsonObject.getJSONObject("data"); + List list = + substationService.list(new LambdaQueryWrapper().eq(Substation::getStationIp, + httpServerConfig.getPatrolIp())); + List list1 = algorithmModelService.list(); + if (list1.size() > 0) { + AlgorithmModel algorithmModel = list1.get(0); + JSONObject jsonObject2 = new JSONObject(); + jsonObject2.put("type", 0); + jsonObject2.put("version", algorithmModel.getVersion()); + JSONArray jsonArray = new JSONArray(); + jsonArray.add(jsonObject2); + data.put("version_info", jsonArray.toString()); + } + if (list.size() > 0) { + Substation substation = list.get(0); + data.put("province_name", substation.getProvinceName()); + data.put("city_name", substation.getCityName()); + data.put("volt_level", substation.getVoltLevel()); + data.put("section_id", substation.getSectionId()); + data.put("section name", substation.getSectionName()); + // data.put("station_id", substation.getStationId()); + data.put("station_name", substation.getStationName()); + + } + // TODO 缺少变电站信息 + // 发送资源到上级系统 + // this.sendTaskData("313", "", "", data.toString()); + + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootSessionCache.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootSessionCache.java new file mode 100644 index 0000000..9507ded --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/BootSessionCache.java @@ -0,0 +1,42 @@ +package com.yfd.platform.component.nettyclient; + +import cn.hutool.core.util.ObjUtil; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * TCP通道缓存 + */ +public class BootSessionCache { + + public static volatile Map requestSessionID = new ConcurrentHashMap(); + + public static volatile Map responeSessionID = new ConcurrentHashMap(); + + public static long getRequestSessionID(){ + Long id=0L; + if(ObjUtil.isEmpty(BootSessionCache.requestSessionID.get("requestid"))){ + BootSessionCache.requestSessionID.put("requestid",1L); + return 1L; + } + id=Long.parseLong(BootSessionCache.requestSessionID.get("requestid").toString()); + id=id+1; + BootSessionCache.requestSessionID.put("requestid",id); + return id; + } + + public static long getResponseSessionID(){ + Long id=0L; + if(ObjUtil.isEmpty(BootSessionCache.responeSessionID.get("responeid"))){ + BootSessionCache.responeSessionID.put("responeid",1L); + return 1L; + } + id=Long.parseLong(BootSessionCache.responeSessionID.get("responeid").toString()); + id=id+1; + BootSessionCache.responeSessionID.put("responeid",id); + return id; + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/HeartBeatJob.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/HeartBeatJob.java new file mode 100644 index 0000000..5cd4899 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/HeartBeatJob.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.component.nettyclient; + +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.utils.SpringContextHolder; +import io.netty.util.CharsetUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Resource; +import java.util.Map; + +/** + * @date 2023-05-23 + * 动态执行巡视主机心跳 + */ +@Slf4j +public class HeartBeatJob implements Runnable { + + // 使用定时器发送心跳 + @Resource + private HttpServerConfig httpServerConfig; + private ParentConfig parentConfig; + + public HeartBeatJob(ParentConfig parentConfig) { + this.parentConfig = parentConfig; + this.httpServerConfig = SpringContextHolder.getBean(HttpServerConfig.class); + } + + @SneakyThrows + @Override + public void run() { + String xml = MyXmlUtil.getXml(httpServerConfig.getPatrolServerid(), parentConfig.getParentId(), SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode() + , SystemCode.COMMAND_HEARTBEAT_CODE.getCode(), "", null); + WebSocketServer.sendInfo("robotinfo", xml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getRequestSessionID()); + messageProtocol.setReceiverSerialNo(0L); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if (BootNettyClientChannelCache.channelMapCache.size() > 0) { + for (Map.Entry entry : + BootNettyClientChannelCache.channelMapCache.entrySet()) { + BootNettyClientChannel bootNettyChannel = entry.getValue(); + if (bootNettyChannel != null && bootNettyChannel.getChannel().isOpen()) { + bootNettyChannel.getChannel().writeAndFlush(messageProtocol); + } + else { + BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), + Integer.parseInt(parentConfig.getTcpPort()), parentConfig); + thread.start(); + } + } + } + else { + BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), + Integer.parseInt(parentConfig.getTcpPort()), parentConfig); + thread.start(); + } + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/HeartBeatTask.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/HeartBeatTask.java new file mode 100644 index 0000000..9bedbfc --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/HeartBeatTask.java @@ -0,0 +1,143 @@ +package com.yfd.platform.component.nettyclient; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.time.Instant; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +//import org.springframework.mail.javamail.JavaMailSender; + +/** + * 动态定时任务 + * :用于延时执行任务 或者 重试执行任务 + */ +@Component +public class HeartBeatTask { + + private final Logger logger = LoggerFactory.getLogger(HeartBeatTask.class); + + private ThreadPoolTaskScheduler threadPoolTaskScheduler; + + private final Map> futureMap = new ConcurrentHashMap<>(); + private final Map runnableMap = new ConcurrentHashMap<>(); + + + + + @PostConstruct + public void DynamicTask() { + threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(50); + threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true); + threadPoolTaskScheduler.setAwaitTerminationSeconds(10); + threadPoolTaskScheduler.initialize(); + } + + /** + * 循环执行的任务 + * @param key 任务ID + * @param task 任务 + * @param cycleForCatalog 间隔 毫秒 + * @return + */ + public void startCron(String key, Runnable task, int cycleForCatalog) { + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + } + stop(key); + } + // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 + future = threadPoolTaskScheduler.scheduleAtFixedRate(task, cycleForCatalog); + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + } + + /** + * 延时任务 + * @param key 任务ID + * @param task 任务 + * @param delay 延时 /毫秒 + * @return + */ + public void startDelay(String key, Runnable task, int delay) { + stop(key); + + // 获取执行的时刻 + Instant startInstant = Instant.now().plusMillis(TimeUnit.MILLISECONDS.toMillis(delay)); + + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } + future = threadPoolTaskScheduler.schedule(task, startInstant); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); + } + } + + public boolean stop(String key) { + boolean result = false; + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) { + result = futureMap.get(key).cancel(false); + futureMap.remove(key); + runnableMap.remove(key); + } + return result; + } + + public boolean contains(String key) { + return futureMap.get(key) != null; + } + + public Set getAllKeys() { + return futureMap.keySet(); + } + + public Runnable get(String key) { + return runnableMap.get(key); + } + + /** + * 每五分钟检查失效的任务,并移除 + */ + @Scheduled(cron="0 0/5 * * * ?") + public void execute(){ + if (futureMap.size() > 0) { + for (String key : futureMap.keySet()) { + if (futureMap.get(key).isDone() || futureMap.get(key).isCancelled()) { + futureMap.remove(key); + runnableMap.remove(key); + } + } + } + } + + public boolean isAlive(String key) { + return futureMap.get(key) != null && !futureMap.get(key).isDone() && !futureMap.get(key).isCancelled(); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyDelimiterBasedFrameDecoder.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyDelimiterBasedFrameDecoder.java new file mode 100644 index 0000000..a87f268 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyDelimiterBasedFrameDecoder.java @@ -0,0 +1,41 @@ +package com.yfd.platform.component.nettyclient; + +import com.yfd.platform.component.nettyclient.MyMessageProtocol; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; + +public class MyDelimiterBasedFrameDecoder extends DelimiterBasedFrameDecoder { + + public MyDelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter) { + super(maxFrameLength, delimiter); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + ByteBuf frame = (ByteBuf) super.decode(ctx, in); + MyMessageProtocol msg = new MyMessageProtocol(); + if (frame == null) { + return null; + } + if (frame.readableBytes() >= 19) { + //读取SenderSerialNo字段 + msg.setSenderSerialNo(frame.readLongLE()); + //读取ReceiverSerialNo字段 + msg.setReceiverSerialNo(frame.readLongLE()); + //读取Sessionflag字段 + msg.setSessionflag(frame.readByte()); + //读取length字段 + int length = frame.readIntLE(); + msg.setXmllen(length); + //读取body + byte []bytes = new byte[length]; + frame.readBytes(bytes); + msg.setXmlcontent(bytes); + } + return msg; + + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageDecoder.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageDecoder.java new file mode 100644 index 0000000..b603c8a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageDecoder.java @@ -0,0 +1,110 @@ +package com.yfd.platform.component.nettyclient; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import java.util.List; + +public class MyMessageDecoder extends ByteToMessageDecoder { + private enum State { + WAITING_FOR_START, + READING_MESSAGE + } + + private State state = State.WAITING_FOR_START; + private final int expectedStartFlag = 0xEB90; + private final int expectedEndFlag = 0xEB90; + private int startFlag = 0; + private long sendSessionID = 0; + private long receiveSessionID = 0; + private byte sessionSource = 0; + private int xmlLength = 0; + private ByteBuf cumulatedBuffer; + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + if (cumulatedBuffer == null) { + cumulatedBuffer = in; + } else { + if (cumulatedBuffer.readableBytes() == 0) { + cumulatedBuffer = in; + }else{ + cumulatedBuffer.writeBytes(in); + } + } + while (cumulatedBuffer.readableBytes() >= 2) { + switch (state) { + case WAITING_FOR_START: + startFlag = cumulatedBuffer.readUnsignedShort(); + if (startFlag == expectedStartFlag) { + state = State.READING_MESSAGE; + } else { + // 未找到起始标志符,报文出现错误,重新等待起始标志符 + state = State.WAITING_FOR_START; + } + break; + + case READING_MESSAGE: + if (cumulatedBuffer.readableBytes() >= 19) { + if(xmlLength==0){ + // 读取发送会话序列号 + sendSessionID = cumulatedBuffer.readLongLE(); + // 读取接收会话序列号 + receiveSessionID = cumulatedBuffer.readLongLE(); + // 读取会话源标识 + sessionSource = cumulatedBuffer.readByte(); + // 读取XML的字节长度 + xmlLength = cumulatedBuffer.readIntLE(); + } + + if (cumulatedBuffer.readableBytes() >= xmlLength + 2) { + ByteBuf xmlData = cumulatedBuffer.readSlice(xmlLength); + byte[] xmlContent1 = new byte[xmlLength]; + xmlData.getBytes(0, xmlContent1); + + int endFlag = cumulatedBuffer.readUnsignedShort(); + + if (endFlag == expectedEndFlag) { + // 报文解析完成 + MyMessageProtocol msg = new MyMessageProtocol(); + msg.setSenderSerialNo(sendSessionID); + msg.setReceiverSerialNo(receiveSessionID); + msg.setSessionflag(sessionSource); + msg.setXmllen(xmlLength); + byte[] xmlContent = new byte[xmlLength]; + xmlData.getBytes(0, xmlContent); + msg.setXmlcontent(xmlContent); + out.add(msg); + cumulatedBuffer.clear(); + xmlLength=0; + // 返回到等待状态 + state = State.WAITING_FOR_START; + } else { + // 未找到结束标志符,报文出现错误,重新等待起始标志符 + cumulatedBuffer.clear(); + xmlLength=0; + state = State.WAITING_FOR_START; + } + } else { + // 不足以解析完整的XML数据,等待更多数据 + return; + } + } else { + // 不足以解析完整的报文,等待更多数据 + return; + } + break; + } + } + + // 如果有剩余数据,将其累积到下一个数据包中 + if (cumulatedBuffer.readableBytes() > 0) { + in.clear().writeBytes(cumulatedBuffer); + } else { + in.clear(); + } + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageEncoder.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageEncoder.java new file mode 100644 index 0000000..93d0fb7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageEncoder.java @@ -0,0 +1,21 @@ +package com.yfd.platform.component.nettyclient; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class MyMessageEncoder extends MessageToByteEncoder { + @Override + protected void encode(ChannelHandlerContext ctx, MyMessageProtocol msg, ByteBuf out) throws Exception { + //TCP传输应用协议定义 + byte[] flag=new byte[]{(byte) 0xEB, (byte) 0x90}; + out.writeBytes(flag); + out.writeLongLE(msg.getSenderSerialNo()); + out.writeLongLE(msg.getReceiverSerialNo()); + out.writeByte(msg.getSessionflag()); + out.writeIntLE(msg.getXmllen()); + out.writeBytes(msg.getXmlcontent()); + out.writeBytes(flag); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageProtocol.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageProtocol.java new file mode 100644 index 0000000..3c93d56 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyMessageProtocol.java @@ -0,0 +1,64 @@ +package com.yfd.platform.component.nettyclient; + + +/** + * 自定义协议包 + */ +public class MyMessageProtocol { + //发送会话序列号 + private long senderSerialNo; + + //接收会话序列号 + private long receiverSerialNo; + + //会话源标识 会话请求='0' 会话响应='1' + private int sessionflag; + + //xml 的字节长度int + private int xmllen; + + //交互内容(xml 格式)xml,字符编码为UTF-8 + private byte[] xmlcontent; + + public long getSenderSerialNo() { + return senderSerialNo; + } + + public void setSenderSerialNo(long senderSerialNo) { + this.senderSerialNo = senderSerialNo; + } + + public int getSessionflag() { + return sessionflag; + } + + public void setSessionflag(int sessionflag) { + this.sessionflag = sessionflag; + } + + public long getReceiverSerialNo() { + return receiverSerialNo; + } + + public void setReceiverSerialNo(long receiverSerialNo) { + this.receiverSerialNo = receiverSerialNo; + } + + public int getXmllen() { + return xmllen; + } + + public void setXmllen(int xmllen) { + this.xmllen = xmllen; + } + + public byte[] getXmlcontent() { + return xmlcontent; + } + + public void setXmlcontent(byte[] xmlcontent) { + this.xmlcontent = xmlcontent; + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyXmlUtil.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyXmlUtil.java new file mode 100644 index 0000000..4bc91ed --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/MyXmlUtil.java @@ -0,0 +1,143 @@ +package com.yfd.platform.component.nettyclient; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.XmlUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.w3c.dom.*; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +public class MyXmlUtil { + + public static String getXml(String SendCode, String ReceiveCode, String Type, String Command, String Code, String jsonstr){ + StringBuilder Xml = new StringBuilder(2000); + Xml.append("\n"); + Xml.append("\n"); + Xml.append("").append(SendCode).append("\n"); + Xml.append("").append(ReceiveCode).append("\n"); + Xml.append("").append(Type).append("\n"); + Xml.append("").append(Command).append("\n"); + Xml.append("").append(Code).append("\n"); + Xml.append("\n"); + Xml.append("\n"); + if (JSONUtil.isTypeJSONArray(jsonstr)) { + JSONArray jsonArray = JSONUtil.parseArray(jsonstr); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + Xml.append("\n"); + } + } + if (JSONUtil.isTypeJSONObject(jsonstr)) { + JSONObject jsonObject = JSONUtil.parseObj(jsonstr); + Xml.append("\n"); + } + Xml.append("\n"); + Xml.append(""); + return Xml.toString(); + } + + /********************************** + * 用途说明: 将xml转成Map + * 参数说明 xml xml数据 + * 返回值说明: java.util.Map + ***********************************/ + public static Map xmlToMap(String xml) throws ParserConfigurationException, IOException, + SAXException { + Map map = XmlUtil.xmlToMap(xml); + return xmlToMap(map, xml, "Items", "Item"); + } + + /********************************** + * 用途说明: 将xmlModel转成Map + * 参数说明 xml xml数据 + * 返回值说明: java.util.Map + ***********************************/ + public static Map xmlModelToMap(String xml) throws ParserConfigurationException, IOException, + SAXException { + Map map = new HashMap<>(); + return xmlToMap(map, xml, "Device_Model", "Item"); + } + + /********************************** + * 用途说明: 将xml中的属性解析到map + * 参数说明 xml xml数据 + * 返回值说明: java.util.Map + ***********************************/ + public static Map xmlToMap(Map map, String xml, String element, String nodeName) throws ParserConfigurationException, IOException, SAXException { + // 创建XML解析器工厂 + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(xml))); + Element itemsElement = (Element) doc.getElementsByTagName(element).item(0); + NodeList itemNodes = itemsElement.getElementsByTagName(nodeName); + + // 存放Item数据的集合 + List> itemDataList = new ArrayList<>(); + + // 遍历所有Items下的Item标签 + for (int i = 0; i < itemNodes.getLength(); i++) { + // 获取Item + Element itemElement = (Element) itemNodes.item(i); + NamedNodeMap attributes = itemElement.getAttributes(); + + // 存放Item中值的集合 + Map itemData = new HashMap<>(); + for (int j = 0; j < attributes.getLength(); j++) { + Node attribute = attributes.item(j); + itemData.put(attribute.getNodeName(), attribute.getNodeValue()); + } + + itemDataList.add(itemData); + } + map.put(element, itemDataList); + return map; + } + + /********************************** + * 用途说明: 读取xml文件中的内容 + * 参数说明 filePath 文件路径 + * 返回值说明: java.lang.String + ***********************************/ + public static String readXmlFileContent(String filePath) { + try { + StringBuilder xmlContent = new StringBuilder(); + BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); + String line; + while ((line = bufferedReader.readLine()) != null) { + xmlContent.append(line); + } + bufferedReader.close(); + return xmlContent.toString(); + } catch (IOException e) { + log.error(e.getMessage());; + } + return ""; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyclient/NettyClientRunner.java b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/NettyClientRunner.java new file mode 100644 index 0000000..0a9664c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyclient/NettyClientRunner.java @@ -0,0 +1,32 @@ +package com.yfd.platform.component.nettyclient; + +import com.yfd.platform.config.ParentConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +@Component +@Order(value=20) +public class NettyClientRunner implements CommandLineRunner { + private static final Logger logger = LoggerFactory.getLogger(NettyClientRunner.class); + + + @Autowired + private ParentConfig parentConfig; + + @Override + public void run(String... args) throws Exception { + /** + * 使用异步注解方式启动netty客户端服务 + */ +// BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), +// Integer.parseInt(parentConfig.getTcpPort()), parentConfig); +// thread.start(); + } + + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/BootSessionCache.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/BootSessionCache.java new file mode 100644 index 0000000..cb24fb5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/BootSessionCache.java @@ -0,0 +1,42 @@ +package com.yfd.platform.component.nettyserver; + +import cn.hutool.core.util.ObjUtil; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * TCP通道缓存 + */ +public class BootSessionCache { + + public static volatile Map requestSessionID = new ConcurrentHashMap(); + + public static volatile Map responeSessionID = new ConcurrentHashMap(); + + public static long getRequestSessionID(){ + Long id=0L; + if(ObjUtil.isEmpty(BootSessionCache.requestSessionID.get("requestid"))){ + BootSessionCache.requestSessionID.put("requestid",1L); + return 1L; + } + id=Long.parseLong(BootSessionCache.requestSessionID.get("requestid").toString()); + id=id+1; + BootSessionCache.requestSessionID.put("requestid",id); + return id; + } + + public static long getResponseSessionID(){ + Long id=0L; + if(ObjUtil.isEmpty(BootSessionCache.responeSessionID.get("responeid"))){ + BootSessionCache.responeSessionID.put("responeid",1L); + return 1L; + } + id=Long.parseLong(BootSessionCache.responeSessionID.get("responeid").toString()); + id=id+1; + BootSessionCache.responeSessionID.put("responeid",id); + return id; + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/ChannelMap.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/ChannelMap.java new file mode 100644 index 0000000..d8fe9f0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/ChannelMap.java @@ -0,0 +1,37 @@ +package com.yfd.platform.component.nettyserver; + +import io.netty.channel.Channel; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Description 连接通道保存MAP + * @date 2022/11/27 16:30 + * @Version 1.0 + */ + +public class ChannelMap { + /** + * 存放客户端标识ID(消息ID)与channel的对应关系 + */ + private static volatile ConcurrentHashMap channelMap = null; + + private ChannelMap() { + } + + public static ConcurrentHashMap getChannelMap() { + if (null == channelMap) { + synchronized (ChannelMap.class) { + if (null == channelMap) { + channelMap = new ConcurrentHashMap<>(); + } + } + } + return channelMap; + } + + public static Channel getChannel(String id) { + return getChannelMap().get(id); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MessageParser.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MessageParser.java new file mode 100644 index 0000000..0ba8324 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MessageParser.java @@ -0,0 +1,61 @@ +package com.yfd.platform.component.nettyserver; + +import lombok.extern.slf4j.Slf4j; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; + +@Slf4j +public class MessageParser { + public static void main(String[] args) { + // 给定的报文数据 + String hexData = "3c 7c 3f 4d 56 b4 00 0b ab e0 05 e9 08 00 45 00 01 62 64 9c 40 00 80 06 10 ea c0 a8 01 ab c0 a8 01 14 df f6 27 1b de 43 d4 b9 a8 fa 38 4c 50 18 10 00 58 c2 00 00 eb 90 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 21 01 00 00 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 2e 30 22 20 65 6e 63 6f 64 69 6e 67 3d 22 55 54 46 2d 38 22 20 3f 3e 0d 0a 3c 50 61 74 72 6f 6c 44 65 76 69 63 65 3e 0d 0a 20 20 20 20 3c 53 65 6e 64 43 6f 64 65 3e 33 34 30 32 30 30 30 30 30 31 31 37 30 30 30 30 30 30 30 31 3c 2f 53 65 6e 64 43 6f 64 65 3e 0d 0a 20 20 20 20 3c 52 65 63 65 69 76 65 43 6f 64 65 3e 33 34 30 32 30 30 30 30 30 30 32 30 30 30 30 30 30 30 30 31 3c 2f 52 65 63 65 69 76 65 43 6f 64 65 3e 0d 0a 20 20 20 20 3c 54 79 70 65 3e 32 35 31 3c 2f 54 79 70 65 3e 0d 0a 20 20 20 20 3c 43 6f 6d 6d 61 6e 64 3e 31 3c 2f 43 6f 6d 6d 61 6e 64 3e 0d 0a 20 20 20 20 3c 43 6f 64 65 20 2f 3e 0d 0a 20 20 20 20 3c 54 69 6d 65 3e 32 30 32 33 2d 31 30 2d 31 31 20 31 38 3a 35 36 3a 35 32 3c 2f 54 69 6d 65 3e 0d 0a 20 20 20 20 3c 49 74 65 6d 73 20 2f 3e 0d 0a 3c 2f 50 61 74 72 6f 6c 44 65 76 69 63 65 3e 0d 0a eb 90"; + + // 将十六进制字符串转换为字节数组 + String[] hexValues = hexData.split(" "); + byte[] byteArray = new byte[hexValues.length]; + for (int i = 0; i < hexValues.length; i++) { + byteArray[i] = (byte) Integer.parseInt(hexValues[i], 16); + } + + // 创建ByteBuffer,并设置字节序为小头字节序 + ByteBuffer buffer = ByteBuffer.wrap(byteArray); + buffer.order(ByteOrder.LITTLE_ENDIAN); + // 跳过前 54 个字节 + buffer.position(54); + + // 读取起始标志符 + int startFlag = buffer.getShort() & 0xFFFF; + + // 读取发送会话序列号 + long sendSessionID = buffer.getLong(); + + // 读取接收会话序列号 + long receiveSessionID = buffer.getLong(); + + // 读取会话源标识 + byte sessionSource = buffer.get(); + + // 读取XML的字节长度 + int xmlLength = buffer.getInt(); + + // 读取交互内容(XML) + byte[] xmlData = new byte[xmlLength]; + buffer.get(xmlData); + String xmlContent = new String(xmlData, StandardCharsets.UTF_8); + + // 读取结束标志符 + int endFlag = buffer.getShort() & 0xFFFF; + + // 打印解析结果 + log.info("起始标志符: " + Integer.toHexString(startFlag)); + log.info("发送会话序列号: " + sendSessionID); + log.info("接收会话序列号: " + receiveSessionID); + log.info("会话源标识: 0x" + String.format("%02X", sessionSource)); + log.info("XML的字节长度: " + xmlLength); + log.info("交互内容 (XML):"); + log.info(xmlContent); + log.info("结束标志符: " + Integer.toHexString(endFlag)); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyDelimiterBasedFrameDecoder.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyDelimiterBasedFrameDecoder.java new file mode 100644 index 0000000..a304be7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyDelimiterBasedFrameDecoder.java @@ -0,0 +1,40 @@ +package com.yfd.platform.component.nettyserver; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; + +public class MyDelimiterBasedFrameDecoder extends DelimiterBasedFrameDecoder { + + public MyDelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf delimiter) { + super(maxFrameLength, delimiter); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + ByteBuf frame = (ByteBuf) super.decode(ctx, in); + MyMessageProtocol msg = new MyMessageProtocol(); + if (frame == null) { + return null; + } + if (frame.readableBytes() >= 19) { + //读取SenderSerialNo字段 + msg.setSenderSerialNo(frame.readLongLE()); + //读取ReceiverSerialNo字段 + msg.setReceiverSerialNo(frame.readLongLE()); + //读取Sessionflag字段 + msg.setSessionflag(frame.readByte()); + //读取length字段 + int length = frame.readIntLE(); + msg.setXmllen(length); + //读取body + byte []bytes = new byte[length]; + frame.readBytes(bytes); + msg.setXmlcontent(bytes); + } + return msg; + + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyLengthFieldBasedFrameDecoder.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyLengthFieldBasedFrameDecoder.java new file mode 100644 index 0000000..a237dd0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyLengthFieldBasedFrameDecoder.java @@ -0,0 +1,68 @@ +package com.yfd.platform.component.nettyserver; + +import cn.hutool.core.util.HexUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; + +import java.nio.ByteOrder; + +public class MyLengthFieldBasedFrameDecoder extends LengthFieldBasedFrameDecoder { + + private static final int HEADER_SIZE = 19; + + /** + * @param maxFrameLength 帧的最大长度 + * @param lengthFieldOffset length字段偏移的地址 + * @param lengthFieldLength length字段所占的字节长 + * @param lengthAdjustment 修改帧数据长度字段中定义的值,可以为负数 因为有时候我们习惯把头部记入长度,若为负数,则说明要推后多少个字段 + * @param initialBytesToStrip 解析时候跳过多少个长度 + * @param failFast 为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异 + */ + + + public MyLengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip,boolean failFast) { + super(ByteOrder.LITTLE_ENDIAN, maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip,failFast); + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + ByteBuf frame = (ByteBuf) super.decode(ctx,in); + MyMessageProtocol msg= new MyMessageProtocol(); + if(frame == null){ + return null; + } + if(frame.readableBytes() out) throws Exception { + log.info("进入解码方法" + in.toString()); + if (cumulatedBuffer == null) { + cumulatedBuffer = in; + } else { + if (cumulatedBuffer.readableBytes() == 0) { + cumulatedBuffer = in; + } else { + cumulatedBuffer.writeBytes(in); + } + } + if (cumulatedBuffer.readableBytes() > 1024) { + cumulatedBuffer.clear(); + xmlLength = 0; + state = State.WAITING_FOR_START; + return; + } + while (cumulatedBuffer.readableBytes() >= 2) { + switch (state) { + case WAITING_FOR_START: + startFlag = cumulatedBuffer.readUnsignedShort(); + if (startFlag == expectedStartFlag) { + state = State.READING_MESSAGE; + } else { + // 未找到起始标志符,报文出现错误,重新等待起始标志符 + state = State.WAITING_FOR_START; + } + break; + + case READING_MESSAGE: + if (cumulatedBuffer.readableBytes() >= 19) { + if(xmlLength==0){ + // 读取发送会话序列号 + sendSessionID = cumulatedBuffer.readLongLE(); + // 读取接收会话序列号 + receiveSessionID = cumulatedBuffer.readLongLE(); + // 读取会话源标识 + sessionSource = cumulatedBuffer.readByte(); + // 读取XML的字节长度 + xmlLength = cumulatedBuffer.readIntLE(); + } + + if (cumulatedBuffer.readableBytes() >= xmlLength + 2) { + ByteBuf xmlData = cumulatedBuffer.readSlice(xmlLength); + byte[] xmlContent1 = new byte[xmlLength]; + xmlData.getBytes(0, xmlContent1); + + int endFlag = cumulatedBuffer.readUnsignedShort(); + + if (endFlag == expectedEndFlag) { + // 报文解析完成 + MyMessageProtocol msg = new MyMessageProtocol(); + msg.setSenderSerialNo(sendSessionID); + msg.setReceiverSerialNo(receiveSessionID); + msg.setSessionflag(sessionSource); + msg.setXmllen(xmlLength); + byte[] xmlContent = new byte[xmlLength]; + xmlData.getBytes(0, xmlContent); + msg.setXmlcontent(xmlContent); + out.add(msg); + cumulatedBuffer.clear(); + xmlLength=0; + // 返回到等待状态 + state = State.WAITING_FOR_START; + } else { + // 未找到结束标志符,报文出现错误,重新等待起始标志符 + cumulatedBuffer.clear(); + xmlLength=0; + state = State.WAITING_FOR_START; + } + } else { + // 不足以解析完整的XML数据,等待更多数据 + return; + } + } else { + // 不足以解析完整的报文,等待更多数据 + cumulatedBuffer.clear(); + xmlLength = 0; + state = State.WAITING_FOR_START; + return; + } + break; + } + } + + // 如果有剩余数据,将其累积到下一个数据包中 + if (cumulatedBuffer.readableBytes() > 0) { + in.clear().writeBytes(cumulatedBuffer); + } else { + in.clear(); + } + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyMessageEncoder.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyMessageEncoder.java new file mode 100644 index 0000000..cc1339e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyMessageEncoder.java @@ -0,0 +1,21 @@ +package com.yfd.platform.component.nettyserver; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class MyMessageEncoder extends MessageToByteEncoder { + @Override + protected void encode(ChannelHandlerContext ctx, MyMessageProtocol msg, ByteBuf out) throws Exception { + //TCP传输应用协议定义 + byte[] flag=new byte[]{(byte) 0xEB, (byte) 0x90}; + out.writeBytes(flag); + out.writeLongLE(msg.getSenderSerialNo()); + out.writeLongLE(msg.getReceiverSerialNo()); + out.writeByte(msg.getSessionflag()); + out.writeIntLE(msg.getXmllen()); + out.writeBytes(msg.getXmlcontent()); + out.writeBytes(flag); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyMessageProtocol.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyMessageProtocol.java new file mode 100644 index 0000000..683f014 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyMessageProtocol.java @@ -0,0 +1,67 @@ +package com.yfd.platform.component.nettyserver; + + +import io.netty.buffer.ByteBuf; + +/** + * 自定义协议包 + */ +public class MyMessageProtocol { + //发送会话序列号 + private long senderSerialNo; + + //接收会话序列号 + private long receiverSerialNo; + + //会话源标识 会话请求='0' 会话响应='1' + private int sessionflag; + + //xml 的字节长度int + private int xmllen; + + //交互内容(xml 格式)xml,字符编码为UTF-8 + private byte[] xmlcontent; + + public long getSenderSerialNo() { + return senderSerialNo; + } + + public void setSenderSerialNo(long senderSerialNo) { + this.senderSerialNo = senderSerialNo; + } + + public int getSessionflag() { + return sessionflag; + } + + public void setSessionflag(int sessionflag) { + this.sessionflag = sessionflag; + } + + public long getReceiverSerialNo() { + return receiverSerialNo; + } + + public void setReceiverSerialNo(long receiverSerialNo) { + this.receiverSerialNo = receiverSerialNo; + } + + public int getXmllen() { + return xmllen; + } + + public void setXmllen(int xmllen) { + this.xmllen = xmllen; + } + + + public byte[] getXmlcontent() { + return xmlcontent; + } + + public void setXmlcontent(byte[] xmlcontent) { + this.xmlcontent = xmlcontent; + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyXmlUtil.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyXmlUtil.java new file mode 100644 index 0000000..b3fa311 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/MyXmlUtil.java @@ -0,0 +1,153 @@ +package com.yfd.platform.component.nettyserver; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.XmlUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.w3c.dom.*; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +public class MyXmlUtil { + + public static String getXml(String SendCode, String ReceiveCode, String Type, String Command, String Code, String jsonstr) { + StringBuffer Xml = new StringBuffer(2000); + Xml.append("\n"); + Xml.append("\n"); + Xml.append("" + SendCode + "\n"); + Xml.append("" + ReceiveCode + "\n"); + Xml.append("" + Type + "\n"); + Xml.append("" + Command + "\n"); + Xml.append("" + Code + "\n"); + Xml.append("\n"); + Xml.append("\n"); + if (JSONUtil.isTypeJSONArray(jsonstr)) { + JSONArray jsonArray = JSONUtil.parseArray(jsonstr); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + Xml.append("\n"); + } + } + if (JSONUtil.isTypeJSONObject(jsonstr)) { + JSONObject jsonObject = JSONUtil.parseObj(jsonstr); + Xml.append("\n"); + } + log.info("getXml方法中的jsonstr" + jsonstr); + if (!(JSONUtil.isTypeJSONObject(jsonstr) || JSONUtil.isTypeJSONArray(jsonstr))) { + log.info("进入拼写Item" + jsonstr); + if (StrUtil.isNotBlank(jsonstr)) { + Xml.append("\n"); + } + } + Xml.append("\n"); + Xml.append(""); + return Xml.toString(); + } + + /********************************** + * 用途说明: 将xml转成Map + * 参数说明 xml xml数据 + * 返回值说明: java.util.Map + ***********************************/ + public static Map xmlToMap(String xml) throws ParserConfigurationException, IOException, + SAXException { + Map map = XmlUtil.xmlToMap(xml); + return xmlToMap(map, xml, "Items", "Item"); + } + + /********************************** + * 用途说明: 将xmlModel转成Map + * 参数说明 xml xml数据 + * 返回值说明: java.util.Map + ***********************************/ + public static Map xmlModelToMap(String xml) throws ParserConfigurationException, IOException, + SAXException { + Map map = new HashMap<>(); + return xmlToMap(map, xml, "Device_Model", "Item"); + } + + /********************************** + * 用途说明: 将xml中的属性解析到map + * 参数说明 xml xml数据 + * 返回值说明: java.util.Map + ***********************************/ + public static Map xmlToMap(Map map, String xml, String element, String nodeName) throws ParserConfigurationException, IOException, SAXException { + // 创建XML解析器工厂 + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(xml))); + Element itemsElement = (Element) doc.getElementsByTagName(element).item(0); + NodeList itemNodes = itemsElement.getElementsByTagName(nodeName); + + // 存放Item数据的集合 + List> itemDataList = new ArrayList<>(); + + // 遍历所有Items下的Item标签 + for (int i = 0; i < itemNodes.getLength(); i++) { + // 获取Item + Element itemElement = (Element) itemNodes.item(i); + NamedNodeMap attributes = itemElement.getAttributes(); + + // 存放Item中值的集合 + Map itemData = new HashMap<>(); + for (int j = 0; j < attributes.getLength(); j++) { + Node attribute = attributes.item(j); + itemData.put(attribute.getNodeName(), attribute.getNodeValue()); + } + + itemDataList.add(itemData); + } + map.put(element, itemDataList); + return map; + } + + /********************************** + * 用途说明: 读取xml文件中的内容 + * 参数说明 filePath 文件路径 + * 返回值说明: java.lang.String + ***********************************/ + public static String readXmlFileContent(String filePath) { + try { + StringBuilder xmlContent = new StringBuilder(); + BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); + String line; + while ((line = bufferedReader.readLine()) != null) { + xmlContent.append(line); + } + bufferedReader.close(); + return xmlContent.toString(); + } catch (IOException e) { + log.error(e.getMessage());; + } + return ""; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyHeartTimer.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyHeartTimer.java new file mode 100644 index 0000000..342f3d6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyHeartTimer.java @@ -0,0 +1,71 @@ +package com.yfd.platform.component.nettyserver; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.List; + +/** + * 心跳保持器 + */ +@Component +public class NettyHeartTimer { + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + +// @Scheduled(fixedRate = 10000) + public void timeTask() { + + LocalDateTime startTime = Constant.now; + LocalDateTime endTime = LocalDateTime.now(); + // 指定时区(这里以系统默认时区为例) + ZoneId zoneId = ZoneId.systemDefault(); + + // 将LocalDateTime转换为ZonedDateTime + ZonedDateTime startZoned = startTime.atZone(zoneId); + ZonedDateTime endZoned = endTime.atZone(zoneId); + + // 计算两个ZonedDateTime之间的差异 + Duration duration = Duration.between(startZoned, endZoned); + + // 获取差异的总秒数 + long secondsBetween = duration.getSeconds(); + + if (secondsBetween > 60) { + // 在这里你可以进行清理工作,比如关闭资源 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + String robotIp = "192.168.1.106"; + queryWrapper.eq(SubstationPatroldevice::getIpaddr, robotIp); + List list = substationPatroldeviceService.list(queryWrapper); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("patroldevice_name", substationPatroldevice.getPatroldeviceName()); + jsonObject.putOpt("patroldevice_code", substationPatroldevice.getPatroldeviceCode()); + jsonObject.putOpt("time", DateUtil.now()); + jsonObject.putOpt("type", "301"); + jsonObject.putOpt("value", "1"); + jsonObject.putOpt("unit", ""); + jsonObject.putOpt("value_unit", "异常"); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + } + } + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServer.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServer.java new file mode 100644 index 0000000..d1889f6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServer.java @@ -0,0 +1,77 @@ +package com.yfd.platform.component.nettyserver; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import io.netty.handler.timeout.ReadTimeoutHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteOrder; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +/** + * netty服务器,主要用于与客户端通讯 + */ + +public class NettyServer { + private static final Logger logger = LoggerFactory.getLogger(NettyServer.class); + // private static final int MAX_FRAME_LENGTH = 1024 * 1024 * 1024; // 设置最大消息长度 + private static final byte[] DELIMITER = {(byte) 0xEB, (byte) 0x90}; + private static final int MAX_FRAME_LENGTH = Integer.MAX_VALUE; //最大长度 + private static final int LENGTH_FIELD_OFFSET = 19; //长度偏移 + private static final int LENGTH_FIELD_LENGTH = 4; //长度字段所占的字节数 + private static final int LENGTH_ADJUSTMENT = 0; + private static final int INITIAL_BYTES_TO_STRIP = 0; + private final int port; //监听端口 + public NettyServer(int port) { + this.port = port; + } + + + public void run() throws Exception { + //创建两个线程组 + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); //8个NioEventLoop + + try { + ServerBootstrap b = new ServerBootstrap(); + + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) throws Exception { + //获取到pipeline + ChannelPipeline pipeline = ch.pipeline(); + //向pipeline加入编码器 + pipeline.addLast("encoder", new MyMessageEncoder()); + //向pipeline加入解码器 + pipeline.addLast(new MyDelimiterBasedFrameDecoder(MAX_FRAME_LENGTH, + Unpooled.wrappedBuffer(DELIMITER))); + pipeline.addLast(new MyLengthFieldBasedFrameDecoder(MAX_FRAME_LENGTH, LENGTH_FIELD_OFFSET + , LENGTH_FIELD_LENGTH, LENGTH_ADJUSTMENT, INITIAL_BYTES_TO_STRIP, false)); + // pipeline.addLast("decoder", new MyMessageDecoder()); + //加入自己的业务处理handler + pipeline.addLast(new NettyServerHandler()); +// pipeline.addLast(new ReadTimeoutHandler(60)); + + } + }); + logger.info("Netty TCP server is started!(port:" + port + ")"); + ChannelFuture channelFuture = b.bind(port).sync(); + //监听关闭 + channelFuture.channel().closeFuture().sync(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServerHandler.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServerHandler.java new file mode 100644 index 0000000..01f47c1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServerHandler.java @@ -0,0 +1,1279 @@ +package com.yfd.platform.component.nettyserver; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.*; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.service.*; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.service.IAlarmLogService; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.domain.RobotOfflineLog; +import com.yfd.platform.modules.robotapi.domain.RobotRunLog; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.modules.robotapi.service.IRobotOfflineLogService; +import com.yfd.platform.modules.robotapi.service.IRobotRunLogService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.FtpClient; +import com.yfd.platform.utils.HttpRESTfulUtils; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.CharsetUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.*; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @create 2021/9/9 10:12 + */ +@Slf4j +@Component +@Transactional +public class NettyServerHandler extends SimpleChannelInboundHandler { + + public static NettyServerHandler nettyServerHandler; + + @Resource + private IStationRobotService stationRobotService; + + @Resource + private IWeatherLogService weatherLogService; + + @Resource + private TaskResultMapper taskResultMapper; + + @Resource + private ITaskTodoService taskTodoService; + + @Resource + private ITaskService taskService; + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @Resource + private ISubstationService substationService; + + @Resource + private ISubstationAreaService substationAreaService; + @Resource + private ISubstationBayService substationBayService; + @Resource + private ISubstationMaindeviceService substationMaindeviceService; + @Resource + private ISubstationComponentService substationComponentService; + @Resource + private IAlarmLogService alarmLogService; + + @Resource + private RedisTemplate redisTemplate; + @Resource + private FtpClient ftpClient; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private ParentConfig parentConfig; + @Resource + private IRobotRunLogService robotRunLogService; + @Resource + private IRobotOfflineLogService robotOfflineLogService; + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + @Resource + private IPlatformParentSystemService platformParentSystemService; + private long count = 1; + + @PostConstruct + public void init() { + nettyServerHandler = this; + nettyServerHandler.stationRobotService = this.stationRobotService; + nettyServerHandler.weatherLogService = this.weatherLogService; + nettyServerHandler.taskTodoService = this.taskTodoService; + nettyServerHandler.substationService = this.substationService; + nettyServerHandler.substationPatroldeviceService = this.substationPatroldeviceService; + nettyServerHandler.redisTemplate = this.redisTemplate; + nettyServerHandler.ftpClient = this.ftpClient; + nettyServerHandler.substationDeviceService = this.substationDeviceService; + nettyServerHandler.substationAreaService = this.substationAreaService; + nettyServerHandler.substationBayService = this.substationBayService; + nettyServerHandler.substationMaindeviceService = this.substationMaindeviceService; + nettyServerHandler.substationComponentService = this.substationComponentService; + nettyServerHandler.taskService = this.taskService; + nettyServerHandler.taskResultMapper = this.taskResultMapper; + nettyServerHandler.alarmLogService = this.alarmLogService; + nettyServerHandler.httpServerConfig = this.httpServerConfig; + nettyServerHandler.httpRESTfulUtils = this.httpRESTfulUtils; + nettyServerHandler.parentConfig = this.parentConfig; + nettyServerHandler.robotRunLogService = this.robotRunLogService; + nettyServerHandler.robotOfflineLogService = this.robotOfflineLogService; + nettyServerHandler.sysDictionaryItemsService = this.sysDictionaryItemsService; + nettyServerHandler.platformParentSystemService = this.platformParentSystemService; + } + + /** + * 客户端连接会触发 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + Constant.now = LocalDateTime.now(); + Channel channel = ctx.channel(); + log.info("机器人{}连接成功!", ctx.pipeline().channel().remoteAddress()); + ctx.write("server connected success"); + ctx.flush(); + String stationNodeIP = ctx.channel().remoteAddress().toString().substring(1).split(":")[0]; + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // String robotIp = ctx.channel().remoteAddress().toString().substring(1).split(":")[0]; + queryWrapper.eq(SubstationPatroldevice::getIpaddr, stationNodeIP); + List list = nettyServerHandler.substationPatroldeviceService.list(queryWrapper); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("patroldevice_name", substationPatroldevice.getPatroldeviceName()); + jsonObject.putOpt("patroldevice_code", substationPatroldevice.getPatroldeviceCode()); + jsonObject.putOpt("time", DateUtil.now()); + jsonObject.putOpt("type", "301"); + jsonObject.putOpt("value", "0"); + jsonObject.putOpt("unit", ""); + jsonObject.putOpt("value_unit", "正常"); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + } + ChannelMap.getChannelMap().put(stationNodeIP, ctx.channel()); + } + + /** + * 客户端发消息会触发 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void channelRead0(ChannelHandlerContext ctx, MyMessageProtocol msg) throws Exception { + if (msg.getXmlcontent() == null) { + log.info("客户端发消息会触发消息为空"); + return; + } + // 发送状态信息 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + String robotIp = ctx.channel().remoteAddress().toString().substring(1).split(":")[0]; + lambdaQueryWrapper.eq(SubstationPatroldevice::getIpaddr, robotIp); + List patroldevices = nettyServerHandler.substationPatroldeviceService.list(lambdaQueryWrapper); + if (patroldevices.size() > 0) { + SubstationPatroldevice substationPatroldevice = patroldevices.get(0); + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("patroldevice_name", substationPatroldevice.getPatroldeviceName()); + jsonObject.putOpt("patroldevice_code", substationPatroldevice.getPatroldeviceCode()); + jsonObject.putOpt("time", DateUtil.now()); + jsonObject.putOpt("type", "301"); + jsonObject.putOpt("value", "0"); + jsonObject.putOpt("unit", ""); + jsonObject.putOpt("value_unit", "正常"); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + } + + String xml = new String(msg.getXmlcontent()); + Map map = MyXmlUtil.xmlToMap(xml); + String Type = map.get("Type").toString(); + String Command = map.get("Command").toString(); + String Code = ""; + if (ObjectUtil.isNotEmpty(map.get("Code"))) { + Code = map.get("Code").toString(); + } + // 序列号 + long receiverSerialNo = msg.getReceiverSerialNo(); + if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(Type) && SystemCode.COMMAND_REGISTER_CODE.getCode().equals(Command)) { + //机器人注册成功 + JSONObject jsonObject = new JSONObject(); + if (StrUtil.isNotBlank(nettyServerHandler.parentConfig.getHeartBeatInterval())) { + jsonObject.putOnce("heart_beat_interval", nettyServerHandler.parentConfig.getHeartBeatInterval()); + } + + if (StrUtil.isNotBlank(nettyServerHandler.parentConfig.getPatroldeviceRunInterval())) { + jsonObject.putOnce("patroldevice_run_interval", + nettyServerHandler.parentConfig.getPatroldeviceRunInterval()); + } + + if (StrUtil.isNotBlank(nettyServerHandler.parentConfig.getWeatherInterval())) { + jsonObject.putOnce("env_interval", nettyServerHandler.parentConfig.getWeatherInterval()); + } + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), jsonObject.toString()); + log.info("响应信息" + reponsexml); + sendResponseData(ctx, msg, reponsexml); + log.info("机器人/无人机注册成功!{}", map.get("SendCode").toString()); + } + if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(Type) && SystemCode.COMMAND_HEARTBEAT_CODE.getCode().equals(Command)) { + String sendCode = map.get("SendCode").toString(); + String receiveCode = map.get("ReceiveCode").toString(); + //机器人发送心跳信息 + String reponsexml = MyXmlUtil.getXml(receiveCode, sendCode, SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode() + , SystemCode.COMMAND_RESPONSE_CODE.getCode(), SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + nettyServerHandler.stationRobotService.updateRobotOnline(robotIp, SystemCode.ONLINE_CODE.getCode()); + RobotRunLog robotRunLog = new RobotRunLog(); + robotRunLog.setCode(Code); + robotRunLog.setCommand(Command); + LocalDateTime now = LocalDateTime.now(); + robotRunLog.setReportTime(now); + robotRunLog.setType(Type); + robotRunLog.setXmlContent(xml); + robotRunLog.setRobotCode(sendCode); + robotRunLog.setPatrolServerCode(receiveCode); + List list = + nettyServerHandler.robotRunLogService.list(new LambdaQueryWrapper().eq(RobotRunLog::getPatrolServerCode, receiveCode).eq(RobotRunLog::getRobotCode, sendCode).orderByDesc(RobotRunLog::getReportTime).last("LIMIT 1")); + if (list.size() > 0) { + // 最后一次心跳 + RobotRunLog lastRobotRunLog = list.get(0); + // 最后一次时间 + LocalDateTime lastReportTime = lastRobotRunLog.getReportTime(); + Duration duration = Duration.between(lastReportTime, now); + if (duration.getSeconds() > 4 * 60 * 60) { + List robotOfflineLogs = + nettyServerHandler.robotOfflineLogService.list(new LambdaQueryWrapper().eq(RobotOfflineLog::getPatrolServerCode, receiveCode).eq(RobotOfflineLog::getRobotCode, sendCode).orderByDesc(RobotOfflineLog::getOfflineEndTime).last("LIMIT 1")); + long sum = 1; + if (robotOfflineLogs.size() > 0) { + RobotOfflineLog robotOfflineLog = robotOfflineLogs.get(0); + sum = NumberUtil.parseLong(robotOfflineLog.getOfflineSum()) + 1; + } + //时间差大于4小时,插入一条离线记录 + RobotOfflineLog robotOfflineLog = new RobotOfflineLog(); + robotOfflineLog.setOfflineEndTime(now.minusSeconds(1)); + robotOfflineLog.setOfflineStartTime(lastReportTime); + robotOfflineLog.setOfflineSum(sum + ""); + robotOfflineLog.setPatrolServerCode(receiveCode); + robotOfflineLog.setRobotCode(sendCode); + nettyServerHandler.robotOfflineLogService.save(robotOfflineLog); + } + } + nettyServerHandler.robotRunLogService.save(robotRunLog); + log.info("机器人/无人机心跳保持!{} ,{}", map.get("SendCode").toString(), DateUtil.now()); + } + + if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(Type) && SystemCode.COMMAND_RESPONSE_CODE.getCode().equals(Command)) { + String xmlContent = + nettyServerHandler.redisTemplate.opsForValue().get(Constant.ROBOT_SEND_CODE + receiverSerialNo); + Map xmlMap = MyXmlUtil.xmlToMap(xmlContent); + // 发送的指令 + String responseType = xmlMap.get("Type").toString(); + String responseCommand = xmlMap.get("Command").toString(); + // 检修区域下发 + if (SystemCode.TYPE_EXAMINE_ISSUED_CODE.getCode().equals(responseType) && SystemCode.COMMAND_EXAMINE_CONFIG_CODE.getCode().equals(responseCommand)) { + log.info("检修区域下发!{}", map.toString()); + } + if (SystemCode.TYPE_TASK_ISSUED_CODE.getCode().equals(responseType) && SystemCode.COMMAND_TASK_CONFIG_CODE.getCode().equals(responseCommand)) { + log.info("任务配置响应!{}-{}", map.toString(), DateUtil.now()); + } + } + + if (SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode().equals(Type) && SystemCode.COMMAND_ITEM_RESPONSE_CODE.getCode().equals(Command)) { + log.info("序列号展示" + msg.getReceiverSerialNo()); + String xmlContent = + nettyServerHandler.redisTemplate.opsForValue().get(Constant.ROBOT_SEND_CODE + receiverSerialNo); + Map xmlMap = MyXmlUtil.xmlToMap(xmlContent); + // 发送的指令 + String responseType = xmlMap.get("Type").toString(); + String responseCommand = xmlMap.get("Command").toString(); + List> items = (List>) map.get("Items"); + // 任务启动响应 + if (SystemCode.TYPE_TASK_CONTROL_CODE.getCode().equals(responseType) && SystemCode.COMMAND_TASK_START_CODE.getCode().equals(responseCommand)) { + log.info("任务控制-任务启动响应!{}", map.toString()); + JSONObject jsonObject = new JSONObject(); + Map map1 = items.get(0); + jsonObject.putOnce("task_patrolled_id", map1.get("task_patrolled_id")); +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), +// nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonObject.toString()); + // for (Map item : items) { + // TaskTodo taskTodo = new TaskTodo(); + // String taskPatrolledId = item.get("task_patrolled_id"); + // taskTodo.setTaskTodoId(taskPatrolledId); + // taskTodo.setDatastatus("1"); + // taskTodo.setLastmodifier("机器人系统"); + // taskTodo.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + // // 新增任务执行数据 + // nettyServerHandler.taskTodoService.save(taskTodo); + // } + } + // 任务暂停响应 + if (SystemCode.TYPE_TASK_CONTROL_CODE.getCode().equals(responseType) && SystemCode.COMMAND_TASK_PAUSE_CODE.getCode().equals(responseCommand)) { + JSONObject jsonObject = new JSONObject(); + Map map1 = items.get(0); + jsonObject.putOnce("task_patrolled_id", map1.get("task_patrolled_id")); + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonObject.toString()); + log.info("任务控制-任务暂停响应!{}", map.toString()); + } + // 任务继续响应 + if (SystemCode.TYPE_TASK_CONTROL_CODE.getCode().equals(responseType) && SystemCode.COMMAND_TASK_RESUME_CODE.getCode().equals(responseCommand)) { + JSONObject jsonObject = new JSONObject(); + Map map1 = items.get(0); + jsonObject.putOnce("task_patrolled_id", map1.get("task_patrolled_id")); + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonObject.toString()); + log.info("任务控制-任务继续响应!{}", map.toString()); + } + // 任务停止响应 + if (SystemCode.TYPE_TASK_CONTROL_CODE.getCode().equals(responseType) && SystemCode.COMMAND_TASK_STOP_CODE.getCode().equals(responseCommand)) { + JSONObject jsonObject = new JSONObject(); + Map map1 = items.get(0); + jsonObject.putOnce("task_patrolled_id", map1.get("task_patrolled_id")); + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonObject.toString()); + log.info("任务控制-任务停止响应!{}", map.toString()); + } + // 模型同步 + if (SystemCode.TYPE_MODEL_SYNC_CODE.getCode().equals(responseType) && SystemCode.COMMAND_MODEL_HOST_CODE.getCode().equals(responseCommand)) { + log.info("进入模型同步方法"); + for (Map item : items) { + + if (!item.containsKey(SystemCode.COMMAND_DEVICE_FILE_PATH.getCode())) { + continue; + } + String deviceFilePath = item.get(SystemCode.COMMAND_DEVICE_FILE_PATH.getCode()); + String path = nettyServerHandler.httpServerConfig.getModelPath() + deviceFilePath; + log.info("文件路径" + path); + StringBuilder content = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new FileReader(path))) { + String line; + while ((line = reader.readLine()) != null) { + content.append(line).append("\n"); + } + } catch (IOException e) { + log.error(e.getMessage());; + } + // String xmlContent = content.toString(); + Map modelMap = MyXmlUtil.xmlModelToMap(content.toString()); + List> deviceList = (List>) modelMap.get("Device_Model"); + for (Map device : deviceList) { + // 模型数据解析成点位对象 + SubstationDevice substationDevice = BeanUtil.toBean(device, SubstationDevice.class); + String stationCode = substationDevice.getStationCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Substation::getStationCode, stationCode); + Substation substation = nettyServerHandler.substationService.getOne(queryWrapper); + String stationId = substation.getStationId(); + substationDevice.setStationId(stationId); + substationDevice.setDatastatus("1"); + substationDevice.setLastmodifier("机器人系统"); + substationDevice.setLastmodifydate(LocalDateTime.now()); + substationDevice.setDeviceCode(substationDevice.getDeviceId()); + + // 新增或者修改区域 + String areaId = substationDevice.getAreaId(); + String areaName = substationDevice.getAreaName(); + SubstationArea substationArea = new SubstationArea(); + substationArea.setAreaId(areaId); + substationArea.setAreaName(areaName); + substationArea.setStationCode(stationCode); + substationArea.setStationName(substation.getStationName()); + substationArea.setDatastatus("1"); + substationArea.setLastmodifier("机器人系统"); + substationArea.setLastmodifydate(LocalDateTime.now()); + nettyServerHandler.substationAreaService.saveOrUpdate(substationArea); + // 新增或者修改间隔 + String bayId = substationDevice.getBayId(); + String bayName = substationDevice.getBayName(); + SubstationBay substationBay = new SubstationBay(); + substationBay.setAreaId(areaId); + substationBay.setAreaName(areaName); + substationBay.setBayId(bayId); + substationBay.setBayName(bayName); + substationBay.setStationCode(stationCode); + substationBay.setStationName(substation.getStationName()); + substationBay.setDatastatus("1"); + substationBay.setLastmodifier("机器人系统"); + substationBay.setLastmodifydate(LocalDateTime.now()); + nettyServerHandler.substationBayService.saveOrUpdate(substationBay); + // 新增或修改主设备 + String mainDeviceId = substationDevice.getMainDeviceId(); + String mainDeviceName = substationDevice.getMainDeviceName(); + SubstationMaindevice substationMaindevice = new SubstationMaindevice(); + substationMaindevice.setMainDeviceId(mainDeviceId); + substationMaindevice.setMainDeviceName(mainDeviceName); + substationMaindevice.setDeviceType(substationDevice.getDeviceType()); + substationMaindevice.setStationCode(stationCode); + substationMaindevice.setStationName(substation.getStationName()); + substationMaindevice.setAreaId(areaId); + substationMaindevice.setAreaName(areaName); + substationMaindevice.setBayId(bayId); + substationMaindevice.setBayName(bayName); + substationMaindevice.setDatastatus("1"); + substationMaindevice.setLastmodifier("机器人系统"); + substationMaindevice.setLastmodifydate(LocalDateTime.now()); + nettyServerHandler.substationMaindeviceService.saveOrUpdate(substationMaindevice); + // 新增或修改部件 + String componentId = substationDevice.getComponentId(); + String componentName = substationDevice.getComponentName(); + SubstationComponent substationComponent = new SubstationComponent(); + substationComponent.setComponentId(componentId); + substationComponent.setComponentName(componentName); + substationComponent.setMainDeviceId(mainDeviceId); + substationComponent.setMainDeviceName(mainDeviceName); + substationComponent.setDeviceType(substationDevice.getDeviceType()); + substationComponent.setStationCode(stationCode); + substationComponent.setStationName(substation.getStationName()); + substationComponent.setAreaId(areaId); + substationComponent.setAreaName(areaName); + substationComponent.setBayId(bayId); + substationComponent.setBayName(bayName); + substationComponent.setDatastatus("1"); + substationComponent.setLastmodifier("机器人系统"); + substationComponent.setLastmodifydate(LocalDateTime.now()); + nettyServerHandler.substationComponentService.saveOrUpdate(substationComponent); + // 新增或修改点位 + nettyServerHandler.substationDeviceService.saveOrUpdate(substationDevice); + } + } + log.info("模型同步!{}", map.toString()); + } + // 检修区域下发 + if (SystemCode.TYPE_EXAMINE_ISSUED_CODE.getCode().equals(responseType) && SystemCode.COMMAND_EXAMINE_CONFIG_CODE.getCode().equals(responseCommand)) { + log.info("检修区域下发!{}", map.toString()); + } + // 联动任务下发 + if (SystemCode.TYPE_LINKAGE_TASK_ISSUED_CODE.getCode().equals(responseType) && SystemCode.COMMAND_TASK_CONFIG_CODE.getCode().equals(responseCommand)) { + log.info("联动任务下发!{}", map.toString()); + } + nettyServerHandler.redisTemplate.delete(Constant.ROBOT_SEND_CODE + receiverSerialNo); + } + List list = nettyServerHandler.platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + String parentCode = ""; + if (list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + parentCode = platformParentSystem.getParentCode(); + } + // 机器人或无人机发送状态 + if (SystemCode.TYPE_DEVICE_STATUS_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + if (StrUtil.isNotBlank(parentCode)) { + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); + } + log.info("机器人主动发送自身状态!状态{}", Type); + } + + // 机器人或无人机发送运行数据 + if (SystemCode.TYPE_DEVICE_RUN_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + WebSocketServer.sendInfo("robot_run_data", jsonArray.toString()); + // nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + // nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); + if (StrUtil.isNotBlank(parentCode)) { + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); + } + log.info("机器人主动发送运行数据!状态{}", Type); + } + + + // 巡视设备坐标 + if (SystemCode.TYPE_DEVICE_COORDINATE_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + // nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + // nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); + log.info("机器人或无人机发送设备坐标!状态{}", Type); + if (StrUtil.isNotBlank(parentCode)) { + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); + } + } + + // 巡视路线 + if (SystemCode.TYPE_PATROL_ROUTE_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + // nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + // nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); +// if (StrUtil.isNotBlank(parentCode)) { +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); +// } + log.info("机器人或无人机发送巡视路线!状态{}", Type); + } + + // 告警信息 + if (SystemCode.TYPE_ABNORMAL_ALARM_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + for (Map item : items) { + AlarmLog alarmLog = BeanUtil.toBean(item, AlarmLog.class); + String patroldeviceCode = alarmLog.getPatroldeviceCode(); + List substationPatroldevices = + nettyServerHandler.substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, patroldeviceCode)); + if (substationPatroldevices.size() > 0) { + SubstationPatroldevice substationPatroldevice = substationPatroldevices.get(0); + alarmLog.setStationId(substationPatroldevice.getStationId()); + alarmLog.setStationCode(substationPatroldevice.getStationCode()); + alarmLog.setStationName(substationPatroldevice.getStationName()); + alarmLog.setAreaId(substationPatroldevice.getAreaId()); + alarmLog.setAreaName(substationPatroldevice.getAreaName()); + alarmLog.setTaskAlarmType("3"); + alarmLog.setDatastatus("1"); + alarmLog.setLastmodifier("机器人系统"); + alarmLog.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + JSONObject jsonObject = JSONUtil.parseObj(substationPatroldevice); + WebSocketServer.sendInfo(alarmLog.getStationId(), jsonObject.toString()); + + } + alarmLog.setAlarmDate(item.get("time")); + } + JSONArray jsonArray = JSONUtil.parseArray(items); + if (StrUtil.isNotBlank(parentCode)) { + nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); + } + // nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), +// nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); + log.info("机器人或无人机发送告警信息!状态{}", Type); + } + + // 机器人或无人机主动发送环境信息 + if (SystemCode.TYPE_ENVIRONMENT_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + // nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode") + // .toString(), + // nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, + // jsonArray + // .toString()); + for (Map item : items) { + JSONObject jsonObject = JSONUtil.parseObj(item); + WeatherLog weatherLog = JSONUtil.toBean(jsonObject, WeatherLog.class); + String patroldeviceCode = weatherLog.getPatroldeviceCode(); + SubstationPatroldevice substationPatroldevice = + nettyServerHandler.substationPatroldeviceService.getOne(new + LambdaQueryWrapper().eq + (SubstationPatroldevice::getPatroldeviceCode, patroldeviceCode)); + if (substationPatroldevice != null) { + // 设备id + weatherLog.setPatroldeviceId(substationPatroldevice.getPatroldeviceId()); + // 变电站id + weatherLog.setStationId(substationPatroldevice.getStationId()); + // 变电站编码 + weatherLog.setStationCode(substationPatroldevice.getStationCode()); + // 变电站名称 + weatherLog.setStationName(substationPatroldevice.getStationName()); + weatherLog.setDatastatus("1"); + weatherLog.setLastmodifier(substationPatroldevice.getPatroldeviceName()); + weatherLog.setLastmodifydate(LocalDateTime.now()); + // 同步最新环境数据 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(WeatherLog::getType, weatherLog.getType()).set + (WeatherLog::getDatastatus, 0); + nettyServerHandler.weatherLogService.update(updateWrapper); + nettyServerHandler.weatherLogService.save(weatherLog); + + WebSocketServer.sendInfo("vibe_run_data", jsonObject.toString()); + } + + } + // nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), + // nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray + // .toString()); +// if (StrUtil.isNotBlank(parentCode)) { +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); +// } + log.info("机器人主动发送环境信息!状态{}", Type); + } + + // 任务进度 + if (SystemCode.TYPE_TASK_STATUS_CODE.getCode().equals(Type) && StrUtil.isBlank(Command)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + for (Map item : items) { + TaskTodo taskTodo = BeanUtil.toBean(item, TaskTodo.class); + // 如果用分析主机,加上这个判断 + String taskTodoId = item.get("task_patrolled_id"); + taskTodo.setTaskTodoId(taskTodoId); + String taskCode = taskTodo.getTaskCode(); + Task task = nettyServerHandler.taskService.getOne(new LambdaQueryWrapper().eq(Task::getTaskCode, taskCode)); + if (task == null) { +// Task task1 = new Task(); +// String id = UUID.randomUUID().toString(); +// task1.setTaskId(id); +// task1.setTaskCode(taskTodo.getTaskCode()); +// task1.setTaskName(taskTodo.getTaskName()); +// task1.setTaskClass("2"); +// task1.setFixedStartTime(taskTodo.getPlanStartTime()); +// task1.setTaskType("1"); +// task1.setTaskTodoType("2"); +// task1.setType("1"); +// task1.setLastmodifydate(new Timestamp(System.currentTimeMillis())); +// task1.setLastmodifier("机器人系统"); +// task1.setIsenable("1"); + continue; + } + taskTodo.setTaskId(task.getTaskId()); + taskTodo.setStationCode(task.getStationCode()); + taskTodo.setTaskType(task.getTaskType()); + taskTodo.setType(task.getType()); + taskTodo.setPriority(task.getPriority()); + taskTodo.setRobotCode(task.getRobotCode()); + if ("100".equals(taskTodo.getTaskProgress())) { + taskTodo.setEndTime(DateUtil.now()); + } + String deviceList = task.getDeviceList(); + // String[] split = deviceList.split(","); + List idList = StrUtil.split(deviceList, ","); + int count = + nettyServerHandler.substationDeviceService.count(new LambdaQueryWrapper().in(SubstationDevice::getDeviceId, idList).ne(SubstationDevice::getRecognitionTypeList, "t_100")); + taskTodo.setDeviceSumnum(count); + taskTodo.setDatastatus("1"); + taskTodo.setLastmodifier("机器人系统"); + taskTodo.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + nettyServerHandler.taskTodoService.saveOrUpdate(taskTodo); + // // 判断数据库中存在于此相同的任务,但不是同一时间的,不操作到数据库 + // String compare = StrUtil.sub(taskTodoId, 0, taskTodoId.length() - 6); + // LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // queryWrapper.like(TaskTodo::getTaskTodoId, compare); + // List taskTodoList = nettyServerHandler.taskTodoService.list(queryWrapper); + // if (taskTodoList.size() == 1) { +// TaskTodo taskTodo1 = taskTodoList.get(0); +// if (!taskTodoId.equals(taskTodo1.getTaskId())) { +// return; +// } +// } +// taskTodo.setTaskTodoId(taskTodoId); +// List list = +// nettyServerHandler.taskService.list(new LambdaQueryWrapper().eq(Task::getTaskCode, +// taskTodo.getTaskCode())); +// // 获取任务信息 +// if (list.size() > 0) { +// Task task = list.get(0); +// String deviceList = task.getDeviceList(); +// String[] split = deviceList.split(","); +// taskTodo.setDeviceSumnum(split.length); +// taskTodo.setStationCode(task.getStationCode()); +// taskTodo.setTaskId(task.getTaskId()); +// taskTodo.setTaskCode(task.getTaskCode()); +// taskTodo.setTaskName(task.getTaskName()); +// taskTodo.setTaskType(task.getTaskType()); +// taskTodo.setType(task.getType()); +// taskTodo.setPriority(task.getPriority()); +// taskTodo.setRobotCode(task.getRobotCode()); +// } +// taskTodo.setDatastatus("1"); +// taskTodo.setLastmodifier("机器人系统"); +// taskTodo.setLastmodifydate(new Timestamp(System.currentTimeMillis())); +// String callAnalyse = nettyServerHandler.httpServerConfig.getCallAnalyse(); +// // 判断是否调用分析 +// if ("false".equals(callAnalyse)) { +// nettyServerHandler.taskTodoService.saveOrUpdate(taskTodo); +// } +// if ("true".equals(callAnalyse) && "0".equals(taskTodo.getTaskProgress())) { +// nettyServerHandler.taskTodoService.saveOrUpdate(taskTodo); +// } + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), "msg"); + + } + JSONArray jsonArray = JSONUtil.parseArray(items); + // 向上级发送 +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), +// nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); +// if (StrUtil.isNotBlank(parentCode)) { +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); +// } + log.info("机器人或无人机发送任务进度!状态{}", Type); + } + +// if ("41".equals(Type) && "4".equals(Command)) { +// String loginfo = String.format("巡视主机向机器人[%s]下发了控制巡视任务控制指令!", robotIp); +// //暂停、 恢复 、终止 修改数据库状态; +// //刷新一下界面SSE +// WebSocketServer.sendInfo("taskresult_" + map.get("SendCode").toString(), "msg"); +// } + + // 巡视结果 + if (SystemCode.TYPE_TASK_RESULT_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + for (Map item : items) { + TaskResult taskResult = BeanUtil.toBean(item, TaskResult.class); + String taskTodoId = item.get("task_patrolled_id"); + TaskTodo taskTodo = nettyServerHandler.taskTodoService.getById(taskTodoId); + if (taskTodo == null) { + continue; + } + String deviceId = taskResult.getDeviceId(); + // 获取点位 + SubstationDevice substationDevice = nettyServerHandler.substationDeviceService.getById(deviceId); + taskResult.setStationCode(substationDevice.getStationCode()); + taskResult.setAreaId(substationDevice.getAreaId()); + + String resultId = taskResult.getResultId(); + if (StrUtil.isBlank(resultId)) { + taskResult.setResultId(IdUtil.simpleUUID()); + } + taskResult.setAreaName(substationDevice.getAreaName()); + taskResult.setBayId(substationDevice.getBayId()); + taskResult.setBayName(substationDevice.getBayName()); + taskResult.setMainDeviceId(substationDevice.getMainDeviceId()); + taskResult.setIsAlarm(substationDevice.getIsAlarm()); + taskResult.setRobotCode(taskTodo.getRobotCode()); + taskResult.setMainDeviceName(substationDevice.getMainDeviceName()); + taskResult.setComponentId(substationDevice.getComponentId()); + taskResult.setComponentName(substationDevice.getComponentName()); + String filePath = taskResult.getFilePath(); + String[] split = filePath.split(","); + Thread.sleep(3000); + String filePath1 = split[0]; + String filePath2 = filePath1.replace(".jpeg", ".jpg"); + cn.hutool.core.io.FileUtil.touch(nettyServerHandler.httpServerConfig.getSnapFilePath() + filePath2); + cn.hutool.core.io.FileUtil.copy(nettyServerHandler.httpServerConfig.getSnapFilePath() + filePath1, + nettyServerHandler.httpServerConfig.getSnapFilePath() + filePath2, true); + if (split.length == 1) { + taskResult.setFilePath(URLEncoder.encode(filePath2, "utf-8")); + taskResult.setDefectFilePath(URLEncoder.encode(filePath2, "utf-8")); + } + if (split.length == 2) { + taskResult.setFilePath(URLEncoder.encode(filePath2, "utf-8")); + taskResult.setDefectFilePath(URLEncoder.encode(split[1], "utf-8")); + } + taskResult.setTaskTodoId(taskTodoId); + String recognitionType = taskResult.getRecognitionType(); + taskResult.setDesc(taskResult.getValue()); + if ("1".equals(recognitionType)) { + taskResult.setValueType("meter"); + } else if ("4".equals(recognitionType)) { + taskResult.setValueType("infrared"); + } else { + List> mapList = + nettyServerHandler.taskResultMapper.getValueByDeviceId(deviceId); + if (mapList.size() > 0) { + Map dataResult = mapList.get(0); + log.info("========================" + dataResult.toString()); + String dataResultId = dataResult.get("dataResultId").toString(); + List sysDictionaryItems = + nettyServerHandler.sysDictionaryItemsService.list(new LambdaQueryWrapper().eq(SysDictionaryItems::getDictId, "f6c757b42cbcd9281fe4a53d1f8cb4fa").eq(SysDictionaryItems::getItemCode, dataResultId)); + if (sysDictionaryItems.size() > 0) { + SysDictionaryItems dictionaryItems = sysDictionaryItems.get(0); + log.info("========================" + dictionaryItems.getCustom1()); + String results = dictionaryItems.getCustom1(); + JSONArray jsonArray = JSONUtil.parseArray(results); + List minVal = + jsonArray.stream().filter(j -> JSONUtil.parseObj(j).getStr("MinVal").equals(taskResult.getValue())).collect(Collectors.toList()); + JSONObject o = JSONUtil.parseObj(minVal.get(0)); + String showInfo = o.getStr("ShowInfo"); + log.info("========================" + showInfo); + taskResult.setDesc(showInfo); + } + } + } + String deviceCode = substationDevice.getDeviceCode(); + List codeList = Arrays.asList("50010", "50011", "50024", "50025", "50040","50041"); + boolean iscall=true; + if(codeList.contains(deviceCode)){ + taskResult.setValue("1"); + taskResult.setDesc(""); + taskResult.setValid("1"); + iscall=false; + } + + taskResult.setConf(taskResult.getValue()); + taskResult.setPatroldeviceDate(taskResult.getTime()); +// taskResult.setDesc(taskResult.getValue()); + taskResult.setFlag("2"); + taskResult.setDatastatus("1"); + taskResult.setLastmodifier("机器人系统"); + taskResult.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // queryWrapper.eq(TaskResult::getTaskTodoId, taskResult.getTaskTodoId()).eq + // (TaskResult::getDeviceId, + // taskResult.getDeviceId()).eq(TaskResult::getTime, taskResult.getTime()); + queryWrapper.eq(TaskResult::getTaskTodoId, taskResult.getTaskTodoId()).eq(TaskResult::getDeviceId, + taskResult.getDeviceId()); + List taskResultList = nettyServerHandler.taskResultMapper.selectList(queryWrapper); + // if ("true".equals(nettyServerHandler.httpServerConfig.getCallAnalyse())) { + // taskResult.setFlag("0"); + // taskResult.setValue(""); + // taskResult.setUnit(""); + // taskResult.setValid(""); + // taskResult.setCustom1(item.toString()); + // } + String callAnalyse = nettyServerHandler.httpServerConfig.getCallAnalyse(); + try { + if (iscall&&"true".equals(callAnalyse) && "4".equals(taskResult.getRecognitionType())) { + callAnalyse = "false"; + String isAlarm = substationDevice.getIsAlarm(); + if ("1".equals(isAlarm)) { + String isNormal; + String alarmlevel = "4"; + //温度 最高温度,最低温度(以逗号分隔) + String highTemp = taskResult.getValue(); + double resultvalue = Double.parseDouble(highTemp); + if (ObjUtil.isEmpty(substationDevice.getEarlyMin()) || ObjUtil.isEmpty(substationDevice.getCriticalMax())) { + //未设置上下限,判定为正常 + //判定为正常 + isNormal = "1"; + } else { + if (NumberUtil.compare(resultvalue, substationDevice.getEarlyMin()) >= 0 && NumberUtil.compare(resultvalue, + substationDevice.getEarlyMax()) < 0) { + isNormal = "0"; + alarmlevel = "1"; + } else if (NumberUtil.compare(resultvalue, substationDevice.getSameMin()) >= 0 && NumberUtil.compare(resultvalue, + substationDevice.getSameMax()) < 0) { + isNormal = "0"; + alarmlevel = "2"; + } else if (NumberUtil.compare(resultvalue, substationDevice.getSeriousMin()) >= 0 && NumberUtil.compare(resultvalue, + substationDevice.getSeriousMax()) < 0) { + isNormal = "0"; + alarmlevel = "3"; + } else if (NumberUtil.compare(resultvalue, substationDevice.getCriticalMin()) >= 0 && NumberUtil.compare(resultvalue, + substationDevice.getCriticalMax()) < 0) { + isNormal = "0"; + alarmlevel = "4"; + } else { + //判定为正常 + isNormal = "1"; + } + } + if ("0".equals(isNormal)) { + taskResult.setValid("2"); + Map map1 = new HashMap<>(); + map1.put("alarmlevel", alarmlevel); + map1.put("alarmtype", "1"); + createTaskAlarmLog(taskResult, map1); + } + } + if (taskResultList.size() > 0) { + TaskResult taskResult1 = taskResultList.get(0); + taskResult.setResultId(taskResult1.getResultId()); + } +// nettyServerHandler.alarmLogService.sendNonCoherentMsg1(taskResult, 0); + } + if (taskResultList.size() > 0) { + TaskResult taskResult1 = taskResultList.get(0); + taskResult.setResultId(taskResult1.getResultId()); + nettyServerHandler.taskResultMapper.updateById(taskResult); + } else { + nettyServerHandler.taskResultMapper.insert(taskResult); + } + + if (iscall&&"true".equals(callAnalyse) && !"4".equals(taskResult.getRecognitionType())) { + com.alibaba.fastjson.JSONObject customParams = new com.alibaba.fastjson.JSONObject(); + //表计类型 + if (ObjectUtil.isNotEmpty(substationDevice.getMeterType())) { + customParams.put("meterType", substationDevice.getMeterType()); + + } + String outsideAngle = substationDevice.getOutsideAngle(); + if (StrUtil.isNotEmpty(outsideAngle)) { + customParams.put("TempAngleList", outsideAngle); + } + + if (ObjectUtil.isNotEmpty(substationDevice.getOutsideAngle())) { + if (JSONUtil.isTypeJSONArray(substationDevice.getOutsideAngle())) { + JSONArray jsonArray = JSONUtil.parseArray(substationDevice.getOutsideAngle()); + customParams.put("TempAngleList", jsonArray); + } + + } + + //基准图 + if (ObjUtil.isNotEmpty(taskResult.getPatroldeviceBaseimage())) { + customParams.put("imageNormalUrlPath", taskResult.getPatroldeviceBaseimage()); + } + //有效识别区域 + if (ObjUtil.isNotEmpty(taskResult.getPatroldeviceEffectiveregion())) { + customParams.put("effectiveregion", taskResult.getPatroldeviceEffectiveregion()); + } + + JSONArray typelist = new JSONArray(); + + if (StrUtil.isNotEmpty(substationDevice.getPictureAnalysisTypeList())) { + com.alibaba.fastjson.JSONObject jobj = + JSON.parseObject(substationDevice.getPictureAnalysisTypeList()); + if (ObjectUtil.isNotEmpty(jobj.getString("PictureAnalysisType"))) { + if ("meter".equals(jobj.getString("PictureAnalysisType"))) { + customParams.put("meterType", substationDevice.getOutsideFeature()); + } + typelist.add(jobj.getString("PictureAnalysisType")); + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDefectAnalysisType"))) { + typelist.add(jobj.getString("PictureDefectAnalysisType")); + } + + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDefectAnalysisType"))) { + JSONArray defectresult = new JSONArray(); + List pictureType = StrUtil.split(jobj.getString("PictureDefectAnalysisType"), ","); + List> pictureDefectAnalysisType = + nettyServerHandler.sysDictionaryItemsService.getDeviceByType( + "PictureDefectAnalysisType"); + // 获取到子项 + List itemCodeList = + pictureDefectAnalysisType.stream().filter(p -> pictureType.contains(p.get( + "itemcode").toString())).map(m -> m.get("custom1").toString()).collect(Collectors.toList()); + + for (String code : itemCodeList) { + JSONArray jsonArray = JSONUtil.parseArray(code); + defectresult.addAll(jsonArray); + List keyList = + jsonArray.stream().map(j -> JSONUtil.parseObj(j).getStr("key")).collect(Collectors.toList()); + typelist.addAll(keyList); + } + if (defectresult.size() > 0) { + //缺陷识别结果类型 + customParams.put("defectresulttypes", defectresult); + } + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDiscriminateAnalysisType"))) { + typelist.add(jobj.getString("PictureDiscriminateAnalysisType")); + } + } + if (typelist.size() == 0) { + throw new Exception("未设置识别方式,不能进行图形识别!"); + } + com.alibaba.fastjson.JSONObject callresult = + nettyServerHandler.httpRESTfulUtils.callPicAnalyse(taskResult + .getTaskTodoId(), + taskResult.getResultId(), JSONUtil.toJsonStr(typelist), + JSONUtil.toJsonStr(customParams), filePath2); + // com.alibaba.fastjson.JSONObject callresult = + // nettyServerHandler.httpRESTfulUtils.callPicAnalyse(taskResult.getTaskTodoId(), + // taskResult.getResultId(), filePath2, substationDevice.getDeviceName()); + } + + WebSocketServer.sendInfo("taskresult_" + taskResult.getStationCode(), "msg"); + }catch (Exception e){ + log.error(e.getMessage());; + } + + } + // 向上级发送 +// JSONArray jsonArray = JSONUtil.parseArray(items); + +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), +// nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray +// .toString()); +// if (StrUtil.isNotBlank(parentCode)) { +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), parentCode, Type, Command, Code, jsonArray.toString()); +// } + log.info("机器人或无人机发送巡视结果!状态{}", Type); + } + + + // 无人机机巢发送运行数据 + if (SystemCode.TYPE_NEST_RUN_CODE.getCode().equals(Type)) { + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + // 向上级发送 +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); + log.info("无人机机巢主动发送自身状态!状态{}", Type); + } + + // 无人机机巢状态数据 + if (SystemCode.TYPE_NEST_STATUS_CODE.getCode().equals(Type)) { + log.info("无人机机巢主动发送自身状态!状态{}", Type); + List> items = (List>) map.get("Items"); + JSONArray jsonArray = JSONUtil.parseArray(items); + // 向上级发送 +// nettyServerHandler.stationRobotService.sendParentData(map.get("ReceiveCode").toString(), nettyServerHandler.parentConfig.getParentId(), Type, Command, Code, jsonArray.toString()); + // 机器人响应 + String reponsexml = MyXmlUtil.getXml(map.get("ReceiveCode").toString(), map.get("SendCode").toString(), + SystemCode.TYPE_SYSTEM_MESSAGE_CODE.getCode(), SystemCode.COMMAND_RESPONSE_CODE.getCode(), + SystemCode.SUCCESS_STATUS_CODE.getCode(), null); + sendResponseData(ctx, msg, reponsexml); + } + + } + + /********************************** + * 用途说明: 发送响应数据 + * 参数说明 ctx + * 参数说明 msg + * 参数说明 reponsexml + * 返回值说明: void + ***********************************/ + public void sendResponseData(ChannelHandlerContext ctx, MyMessageProtocol msg, String reponsexml) { + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getResponseSessionID()); + messageProtocol.setReceiverSerialNo(msg.getSenderSerialNo()); + messageProtocol.setSessionflag(1); + messageProtocol.setXmllen(reponsexml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(reponsexml.getBytes(CharsetUtil.UTF_8)); + ctx.write(messageProtocol); + ctx.flush(); + } + + /********************************** + * 用途说明: 生成报警记录(点位) + * 参数说明 taskResult + * 参数说明 map + * 返回值说明: boolean + ***********************************/ + private boolean createTaskAlarmLog(TaskResult taskResult, Map map) throws UnsupportedEncodingException { + AlarmLog alarmLog = new AlarmLog(); + alarmLog.setId(IdUtil.fastSimpleUUID()); + String deviceid = taskResult.getDeviceId(); + SubstationDevice device = nettyServerHandler.substationDeviceService.getById(deviceid); + String alarmDate = DateUtil.now(); + Substation substation = nettyServerHandler.substationService.getOne(new LambdaQueryWrapper(). + eq(Substation::getStationCode, device.getStationCode())); + alarmLog.setStationCode(device.getStationCode()); + alarmLog.setStationId(substation.getStationId()); + alarmLog.setStationName(substation.getStationName()); + alarmLog.setAreaId(taskResult.getAreaId()); + alarmLog.setAreaName(taskResult.getAreaName()); + alarmLog.setBayId(taskResult.getBayId()); + alarmLog.setBayName(taskResult.getBayName()); + alarmLog.setMainDeviceId(taskResult.getMainDeviceId()); + alarmLog.setMainDeviceName(taskResult.getMainDeviceName()); + alarmLog.setComponentId(taskResult.getComponentId()); + alarmLog.setComponentName(taskResult.getComponentName()); + alarmLog.setDeviceId(taskResult.getDeviceId()); + alarmLog.setDeviceName(taskResult.getDeviceName()); + alarmLog.setPatroldeviceCode(taskResult.getPatroldeviceCode()); + alarmLog.setPatroldeviceName(taskResult.getPatroldeviceName()); + alarmLog.setMaterialId(taskResult.getMaterialId()); + TaskTodo taskTodo = nettyServerHandler.taskTodoService.getById(taskResult.getTaskTodoId()); + alarmLog.setTaskId(taskTodo.getTaskId()); + alarmLog.setTaskPatrolledId(taskResult.getTaskTodoId()); + alarmLog.setTaskResultId(taskResult.getResultId()); + alarmLog.setTaskCode(taskResult.getTaskCode()); + alarmLog.setTaskName(taskResult.getTaskName()); + alarmLog.setTaskAlarmType("1"); //'1:巡视任务告警;2:静默任务告警,3:巡视设备告警', + //alarmLog.setMonitorType("");//非静默监视不填 + alarmLog.setAlarmType(ObjUtil.isNotEmpty(map.get("alarmtype")) ? map.get("alarmtype").toString() : ""); + //'字典表<1>: = 超温报警<2>: = 温升报警<3>: = 三相温差报警<4>: = 三相对比报警<5>: = 声音异常<6>: = 外观异常<7>: = 仪表越限报警<8>: = 仪表超量程报警<9>: = + // 仪表三相对比<10>: = 变位报警', + alarmLog.setAlarmLevel(ObjUtil.isNotEmpty(map.get("alarmlevel")) ? map.get("alarmlevel").toString() : "4");// + // '1:预警;2:一般;3:严重;4;危急', 待判断细分 + alarmLog.setRecognitionType(taskResult.getRecognitionType());//'1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6 + // :闪烁检测11:局放超声波检测12:局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测', + alarmLog.setFileType(taskResult.getFileType());//'1:红外图谱;2:可见光照片;3:音频4:视频5:识别图片,存在多个文件,文件类型用逗号分隔', + //告警文件目录规范:地市名/区域名/变电站名/分类/年月 + String type = "缺陷";//缺陷、判别 + String alarmfilepath = String.format("%s/%s/%s/%s/%s/", + substation.getCityName(), + substation.getSectionName(), + substation.getStationName(), + type, + DateUtil.format(DateUtil.date(), "yyyyMM") + ); + //拷贝原图到报警目录下 + String fullfilename = URLDecoder.decode(taskResult.getFilePath(), "utf-8"); + String alarmfullfilename = alarmfilepath + StrUtil.subAfter(fullfilename, "/", true); + FileUtil.copy(nettyServerHandler.httpServerConfig.getSnapFilePath() + fullfilename, + nettyServerHandler.httpServerConfig.getAlarmFilePath() + alarmfullfilename, true); + alarmLog.setFilePath(URLEncoder.encode(alarmfullfilename, "utf-8"));//原图文件名称(含路径) + alarmLog.setDefectType(taskResult.getValueType());//'缺陷类别' + //拷贝识别图到报警目录下 + if (ObjUtil.isNotEmpty(taskResult.getDefectFilePath())) { + String fulldefectfilename = URLDecoder.decode(taskResult.getDefectFilePath(), "utf-8"); + String alarmfilename = StrUtil.replace(StrUtil.subAfter(fulldefectfilename, "/", true), "识别", "缺陷告警"); + String fullalarmfile = alarmfilepath + alarmfilename; + FileUtil.copy(nettyServerHandler.httpServerConfig.getSnapFilePath() + fulldefectfilename, + nettyServerHandler.httpServerConfig.getAlarmFilePath() + fullalarmfile, + true); + alarmLog.setDefectFilePath(URLEncoder.encode(fullalarmfile, "utf-8"));//标注了识别框和信息 + } + alarmLog.setValue(taskResult.getValue());//图形识别结果(值) + alarmLog.setUnit(taskResult.getUnit()); + String alarminfo = ""; + String value = taskResult.getValue(); + String unit = ""; + if (StrUtil.isNotBlank(taskResult.getUnit())) { + unit = taskResult.getUnit(); + } + if ("meter".equals(taskResult.getValueType())) { + alarminfo = String.format("%s-%s的%s,识别结果为%s%s,判定为[%s]超限缺陷!", taskResult.getMainDeviceName(), + taskResult.getComponentName(), taskResult.getDeviceName(), value, + unit, taskResult.getDesc()); + } + if ("infrared".equals(taskResult.getValueType())) { + alarminfo = String.format("%s-%s的%s,识别温度:%s单位:%s,判定为[%s]超限缺陷!", taskResult.getMainDeviceName(), + taskResult.getComponentName(), taskResult.getDeviceName(), value, + unit, taskResult.getDesc()); + } + if ("sound".equals(taskResult.getValueType())) { + alarminfo = "声音检测异常!"; + } + if (StrUtil.isBlank(alarminfo)) { + alarminfo = String.format("%s-%s的%s,识别结果为%s%s,判定为[%s]异常!", taskResult.getMainDeviceName(), + taskResult.getComponentName(), taskResult.getDeviceName(), value, unit + , taskResult.getDesc()); + } + alarmLog.setContent(alarminfo);//告警描述信息 + alarmLog.setAlarmDate(alarmDate);//发送报警时间 + alarmLog.setCheckFlag("0");//'0:未核查;1:已审核;2.已修正' + alarmLog.setRectangle(taskResult.getRectangle());//'格式:x1,y1,x2y2;x3,y3,x4,y4;多个报警框的左上角和右下角坐标' + alarmLog.setDatastatus("1"); //初始创建 + alarmLog.setLastmodifier("系统创建"); + alarmLog.setLastmodifydate(new Timestamp(DateUtil.current())); + nettyServerHandler.alarmLogService.save(alarmLog);//保存此报警数据 + LocalDateTime delayedAlarmDate = device.getDelayedAlarmDate(); + // 如果当前时间小于报警延迟时间则不报警 + if (delayedAlarmDate != null) { + LocalDateTime parse = LocalDateTimeUtil.parse(alarmDate, "yyyy-MM-dd HH:mm:ss"); + if (!parse.isAfter(delayedAlarmDate)) { + return true; + } + } + Map logMap = BeanUtil.beanToMap(alarmLog); + String stationId = alarmLog.getStationId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getStationId, stationId).eq(AlarmLog::getCheckFlag + , 0); + int count = nettyServerHandler.alarmLogService.count(queryWrapper); + logMap.put("alarmCount", count); + JSONObject jsonObject = new JSONObject(logMap); + String stationCode = substation.getStationCode(); + jsonObject.putOpt("deviceid", jsonObject.getStr("deviceId")); + // if (ObjectUtil.isNotEmpty(map.get("patroldeviceCode")) && ObjectUtil.isNotEmpty(map.get( + // "patroldeviceChannelcode"))) { + // jsonObject.putOpt("channelId", map.get("patroldeviceChannelcode").toString()); + // jsonObject.putOpt("deviceId", map.get("patroldeviceCode").toString()); + // } + jsonObject.putOpt("channelId", taskResult.getPatroldeviceChannelcode()); + jsonObject.putOpt("deviceId", taskResult.getPatroldeviceCode()); + // 告警阈值 + jsonObject.putOpt("earlyMin", device.getEarlyMin()); + jsonObject.putOpt("earlyMax", device.getEarlyMax()); + jsonObject.putOpt("sameMin", device.getSameMin()); + jsonObject.putOpt("sameMax", device.getSameMax()); + jsonObject.putOpt("seriousMin", device.getSeriousMin()); + jsonObject.putOpt("seriousMax", device.getSeriousMax()); + jsonObject.putOpt("criticalMin", device.getCriticalMin()); + jsonObject.putOpt("criticalMax", device.getCriticalMax()); + // 如果是站端则区域也需要发送告警 + WebSocketServer.sendInfo(stationId, jsonObject.toString()); + return true; + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + // 连接不活跃,可能是客户端或服务器关闭了连接 + log.error("===================Client disconnected from server======================="); + // 在这里你可以进行清理工作,比如关闭资源 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + String robotIp = ctx.channel().remoteAddress().toString().substring(1).split(":")[0]; + queryWrapper.eq(SubstationPatroldevice::getIpaddr, robotIp); + List list = nettyServerHandler.substationPatroldeviceService.list(queryWrapper); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("patroldevice_name", substationPatroldevice.getPatroldeviceName()); + jsonObject.putOpt("patroldevice_code", substationPatroldevice.getPatroldeviceCode()); + jsonObject.putOpt("time", DateUtil.now()); + jsonObject.putOpt("type", "301"); + jsonObject.putOpt("value", "1"); + jsonObject.putOpt("unit", ""); + jsonObject.putOpt("value_unit", "异常"); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + } + } + + /** + * 发生异常触发 + */ + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + String robotIp = ctx.channel().remoteAddress().toString().substring(1).split(":")[0]; + nettyServerHandler.stationRobotService.updateRobotOnline(robotIp, "0"); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getIpaddr, robotIp); + List list = nettyServerHandler.substationPatroldeviceService.list(queryWrapper); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("patroldevice_name", substationPatroldevice.getPatroldeviceName()); + jsonObject.putOpt("patroldevice_code", substationPatroldevice.getPatroldeviceCode()); + jsonObject.putOpt("time", DateUtil.now()); + jsonObject.putOpt("type", "301"); + jsonObject.putOpt("value", "1"); + jsonObject.putOpt("unit", ""); + jsonObject.putOpt("value_unit", "异常"); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + } + ctx.close(); + ChannelMap.getChannelMap().remove(robotIp); + log.error(cause.getMessage()); + log.info("客户端异常断开连接:{}", cause.getMessage()); + } + + +} + + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServerRunner.java b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServerRunner.java new file mode 100644 index 0000000..dc78c90 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyserver/NettyServerRunner.java @@ -0,0 +1,35 @@ +package com.yfd.platform.component.nettyserver; + +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.utils.ExecutionJob; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +@Component +@Order(value=20) +public class NettyServerRunner implements CommandLineRunner { + private static final Logger logger = LoggerFactory.getLogger(NettyServerRunner.class); + @Resource + HttpServerConfig httpServerConfig; + @Override + public void run(String... args) throws Exception { + + ExecutionJob.EXECUTOR.submit(() -> { + try { + new NettyServer(Integer.parseInt(httpServerConfig.getPatrolTcpPort())).run(); + logger.info("Netty TCP server is started!"); + } catch (Exception e) { + logger.error("Netty TCP server start failed!", e); + throw new RuntimeException("Netty TCP server start failed!", e); + } + }); + } + + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/CimFileUtil.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/CimFileUtil.java new file mode 100644 index 0000000..cdf81d1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/CimFileUtil.java @@ -0,0 +1,330 @@ +package com.yfd.platform.component.nettyudpclient; + +import com.alibaba.fastjson2.JSONArray; + +public class CimFileUtil { + + public static String getCimFile(){ + StringBuffer content = new StringBuffer(); + content.append("\n"); + content.append("\n"); + content.append("@序号 站序号 监控索引号 设备名称 设备类型 实物ID 是否联动信号\n"); + content.append("#1\t1\t1042\t智能联动任务\t遥控\t112326\t是\n"); +// content.append("#2\t0001\t1039\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); +// content.append("#1\t1\t1039\t站一/110kV.天文 1096线/501 开关\t遥控\t112324\t是\n"); +// content.append("#2\t1\t1040\t站一/110kV.天文 1096线/5017 接地刀阀\t遥控\t112325\t是\n"); + content.append("\n"); + return content.toString() ; + } + + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/NettyUdpClient.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/NettyUdpClient.java new file mode 100644 index 0000000..12de541 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/NettyUdpClient.java @@ -0,0 +1,233 @@ +package com.yfd.platform.component.nettyudpclient; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import com.yfd.platform.component.nettyudpserver.MyMessageProtocol; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.DatagramPacket; +import io.netty.channel.socket.nio.NioDatagramChannel; +import lombok.extern.slf4j.Slf4j; + +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.nio.charset.Charset; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + * UDP Client + */ +@Slf4j +public class NettyUdpClient { + private static final Charset CHARSET = Charset.forName("GB2312"); + + public static void main(String[] args) throws Exception { + //模拟主辅设备发送联动配置文件 +// testSendLinkFile(); + + //模拟主辅设备发送一键顺控 86 3 103 21 + testSendLinkComand("192.168.1.20", "1", "1060", "9301"); + Thread.sleep(15000); + testSendLinkComand("192.168.1.20","1","1061","9301"); +// Thread.sleep(500); +// testSendLinkComand("192.168.22.1","1","21","9300"); + //模拟主辅设备发送智能联动 +// testSendLinkComand1(); + } + + //模拟主辅设备发送联动信号配置文件 + public static void testSendLinkFile() { + // 创建事件循环组 + EventLoopGroup group = new NioEventLoopGroup(); + try { + // 创建启动器 + Bootstrap b = new Bootstrap(); + // 设置事件循环组,通道类型,广播选项和处理器 + b.group(group) + .channel(NioDatagramChannel.class) + .option(ChannelOption.SO_BROADCAST, true) + .handler(new NettyUdpClientHandler()) + ; + // 绑定本地端口并同步等待通道 + Channel channel = b.bind(8081).sync().channel(); + // 创建一个ByteBuf对象用于存储消息内容 + String filecontent = CimFileUtil.getCimFile(); + byte[] content = filecontent.getBytes("GB2312"); + int k = content.length / 240; + for (int i = 0; i <= k; i++) { + int nextflag = 0; + if (i < k) { + nextflag = 1; + } + ByteBuf buf = NettyUdpClient.createFileFramebuf(nextflag, i, i * 240); + channel.writeAndFlush(new DatagramPacket(buf, new InetSocketAddress("192.168.1.20", 9300))); + } + + + // 等待通道关闭 + channel.closeFuture().sync(); + } catch (InterruptedException e) { + log.error(e.getMessage());; + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage());; + } finally { + // 释放资源 + group.shutdownGracefully(); + } + } + + public static ByteBuf createFileFramebuf(int nextflag, int frameno, int startlocation) throws UnsupportedEncodingException { + ByteBuf buf = Unpooled.buffer(); + byte[] frametype = new byte[]{(byte) 0x43, (byte) 0x90, (byte) 0xEB, (byte) 0x90, (byte) 0xEB}; + buf.writeBytes(frametype); + buf.writeShortLE(1);//站序号 + buf.writeByte(nextflag);//后续位标志 0:无后续帧;1:有后续帧 + buf.writeShortLE(frameno);//帧序号 0 开始,后续累加 1 + buf.writeIntLE(startlocation);//起始传输位置 本帧传输的文件起始地址在全部文件中的位置 + // 写入CIM文件内容 + String filecontent = CimFileUtil.getCimFile(); + byte[] content = filecontent.getBytes(CHARSET); + if (frameno == 0 && nextflag == 0) {//是第一帧,并且无后续帧 + int length = content.length; + byte[] framecontent = new byte[length]; + System.arraycopy(content, startlocation, framecontent, 0, length); + buf.writeByte(framecontent.length); + buf.writeBytes(framecontent); + short Totallength = Short.parseShort(String.valueOf(10 + framecontent.length)); + buf.writeByte(Totallength); //校验文件流 + } else if (frameno == 0 && nextflag > 0) {//大于第一帧,并且有后续帧 + byte[] framecontent = new byte[240]; + System.arraycopy(content, startlocation, framecontent, 0, 240); + buf.writeByte(framecontent.length); + buf.writeBytes(framecontent); + short Totallength = Short.parseShort(String.valueOf(10 + framecontent.length)); + buf.writeByte(Totallength); //校验文件流 + } else if (frameno > 0 && nextflag > 0) {//大于第一帧,并且有后续帧 + byte[] framecontent = new byte[240]; + System.arraycopy(content, startlocation, framecontent, 0, 240); + buf.writeByte(framecontent.length); + buf.writeBytes(framecontent); + short Totallength = Short.parseShort(String.valueOf(10 + framecontent.length)); + buf.writeByte(Totallength); //校验文件流 + } else if (frameno > 0 && nextflag == 0) {//大于第一帧,并且无后续帧 + int length = content.length - frameno * 240; + byte[] framecontent = new byte[length]; + System.arraycopy(content, startlocation, framecontent, 0, length); + buf.writeByte(framecontent.length); + buf.writeBytes(framecontent); + short Totallength = Short.parseShort(String.valueOf(10 + framecontent.length)); + buf.writeByte(Totallength); //校验文件流 + } + return buf; + } + + //模拟主辅设备发送联动信息 + public static void testSendLinkComand(String ip, String no, String index, String port) { + // 创建事件循环组 + EventLoopGroup group = new NioEventLoopGroup(); + try { + // 创建启动器 + Bootstrap b = new Bootstrap(); + // 设置事件循环组,通道类型,广播选项和处理器 + b.group(group) + .channel(NioDatagramChannel.class) + .option(ChannelOption.SO_BROADCAST, true) + .handler(new NettyUdpClientHandler()) + ; + // 绑定本地端口并同步等待通道 + Channel channel = b.bind(8081).sync().channel(); + //EB90EB9055 + // 创建一个ByteBuf对象用于存储消息内容 + ByteBuf buf = Unpooled.buffer(); + byte[] frametype = new byte[]{(byte) 0xEB, (byte) 0x90, (byte) 0xEB, (byte) 0x90, (byte) 0x55}; + buf.writeBytes(frametype); + + //示例数据:1 1039 0 动作 保护跳闸 2022-02-01 08:18:55.356 + buf.writeShortLE(Integer.parseInt(no)); + buf.writeShortLE(Integer.parseInt(index)); + buf.writeByte(0); + // 写入第一个字符串长度和内容 + String firstString = "动作-主变保护跳闸类"; + buf.writeByte(firstString.getBytes(CHARSET).length); + buf.writeBytes(firstString.getBytes(CHARSET)); + // 写入第二个字符串长度和内容 + String secondString = "合位"; + buf.writeByte(secondString.getBytes(CHARSET).length); + buf.writeBytes(secondString.getBytes(CHARSET)); + //“2022-02-01 08:18:55.356” [01,64,3B,08,40,80,CB] + byte[] eventtimes = MyMessageProtocol.timeStringToCP56(DateUtil.format(new Timestamp(System.currentTimeMillis()), "yyyy-MM-dd HH:mm:ss.SSS")); + buf.writeBytes(eventtimes); + short Totallength = Short.parseShort(String.valueOf(14 + firstString.getBytes(CHARSET).length + secondString.getBytes(CHARSET).length)); + buf.writeShort(Totallength); + // 向服务端发送消息 + channel.writeAndFlush(new DatagramPacket(buf, new InetSocketAddress(ip, Integer.parseInt(port)))); + // 等待通道关闭 +// channel.closeFuture().sync(); + channel.close(); + } catch (InterruptedException e) { + log.error(e.getMessage());; + } finally { + // 释放资源 + group.shutdownGracefully(); + } + } + + //模拟主辅设备发送联动信息 + public static void testSendLinkComand1() { + // 创建事件循环组 + EventLoopGroup group = new NioEventLoopGroup(); + try { + // 创建启动器 + Bootstrap b = new Bootstrap(); + // 设置事件循环组,通道类型,广播选项和处理器 + b.group(group) + .channel(NioDatagramChannel.class) + .option(ChannelOption.SO_BROADCAST, true) + .handler(new NettyUdpClientHandler()) + ; + // 绑定本地端口并同步等待通道 + Channel channel = b.bind(8081).sync().channel(); + // 创建一个ByteBuf对象用于存储消息内容 + ByteBuf buf = Unpooled.buffer(); + byte[] frametype = new byte[]{(byte) 0x55, (byte) 0x90, (byte) 0xEB, (byte) 0x90, (byte) 0xEB}; + buf.writeBytes(frametype); + + //示例数据:1 1039 0 动作 保护跳闸 2022-02-01 08:18:55.356 + buf.writeShort(1); + buf.writeShort(1042); + buf.writeByte(0); + // 写入第一个字符串长度和内容 + String firstString = "动作"; + buf.writeByte(firstString.getBytes(CHARSET).length); + buf.writeBytes(firstString.getBytes(CHARSET)); + // 写入第二个字符串长度和内容 + String secondString = "启动"; + buf.writeByte(secondString.getBytes(CHARSET).length); + buf.writeBytes(secondString.getBytes(CHARSET)); + //“2022-02-01 08:18:55.356” [01,64,3B,08,40,80,CB] + byte[] eventtimes = + MyMessageProtocol.timeStringToCP56(DateUtil.format(new Timestamp(System.currentTimeMillis()), + "yyyy-MM-dd HH:mm:ss.SSS")); + buf.writeBytes(eventtimes); + short Totallength = + Short.parseShort(String.valueOf(14 + firstString.getBytes(CHARSET).length + secondString.getBytes(CHARSET).length)); + buf.writeShort(Totallength); + // 向服务端发送消息 + channel.writeAndFlush(new DatagramPacket(buf, new InetSocketAddress("192.168.1.7", 9300))); + // 等待通道关闭 + channel.closeFuture().sync(); + } catch (InterruptedException e) { + log.error(e.getMessage());; + } finally { + // 释放资源 + group.shutdownGracefully(); + } + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/NettyUdpClientHandler.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/NettyUdpClientHandler.java new file mode 100644 index 0000000..5fe6e1a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpclient/NettyUdpClientHandler.java @@ -0,0 +1,32 @@ +package com.yfd.platform.component.nettyudpclient; + + +import com.yfd.platform.component.nettyudpserver.NettyUdpServer; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.socket.DatagramPacket; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + + +/** + * + * @create 2021/9/9 10:12 + */ +@Slf4j +@Component +public class NettyUdpClientHandler extends SimpleChannelInboundHandler { + + private static final Logger logger = LoggerFactory.getLogger(NettyUdpServer.class); + + @Override + protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { + + } +} + + + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/MyMessageProtocol.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/MyMessageProtocol.java new file mode 100644 index 0000000..f113ea2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/MyMessageProtocol.java @@ -0,0 +1,98 @@ +package com.yfd.platform.component.nettyudpserver; +import cn.hutool.core.util.StrUtil; +import lombok.Data; + +/** + * 自定义协议包 + */ +@Data +public class MyMessageProtocol { + //类型标识(1-5)5字节 EB90EB9055 + private String frametype; + + //站序号 (6-7) 2字节 + private short stationnum; + + //监控索引号 (8-9) 2字节 + private short monitorindex; + + //监控类型(10) 1字节 0,遥控;1,遥信;2,遥测 + private byte monitortype; + + //属性长度(11) 1字节 + private int attributelength; + + //属性(12-N) + private String attribute; + + //值描述长度(N+1) 1字节 + private int valuelength; + + //值描述(N+2,M) + private String value; + + //事件时标(M+1,M+1) 7个字节 + private String eventtime; + + //和校验 M+8) 1个字节 + private short sumverify; + + // 定义一个方法,将CP56格式的时间byte[],转换为时间格式的字符串 + public static String cp56ToTimeString(byte[] cp56) { + // 检查参数是否合法,如果不是7个字节,返回空字符串 + if (cp56 == null || cp56.length != 7) { + return ""; + } + // 定义一个字符串缓冲区,用来拼接结果 + StringBuilder sb = new StringBuilder(); + // 从第7个字节开始,依次解析每个字节的二进制数,根据CP56格式的规则,还原每个时间单位 + // 并将其转换为十进制数,用冒号或短横线分隔,拼接到字符串缓冲区中 + // 注意年份要加上2000,毫秒要除以1000 + sb.append(2000 + (cp56[6] & 0xFF)).append("-"); // 年 + sb.append(StrUtil.padPre(String.valueOf(Byte.toUnsignedInt(cp56[5])),2,'0')).append("-"); // 月 + sb.append(StrUtil.padPre(String.valueOf(Byte.toUnsignedInt(cp56[4])),2,'0')).append(" "); // 日 + sb.append(StrUtil.padPre(String.valueOf(Byte.toUnsignedInt(cp56[3])),2,'0')).append(":"); // 时 + sb.append(StrUtil.padPre(String.valueOf(Byte.toUnsignedInt(cp56[2])),2,'0')).append(":"); // 分 + int millisecond=0; + millisecond=(cp56[1] & 0xFF)<<8; //将bytes[1]的低8位左移8位,作为结果的高8位 + millisecond |=(cp56[0] & 0xFF); + String temp=String.valueOf(millisecond); + String second=StrUtil.sub(temp,0,temp.length()-3); + sb.append(StrUtil.padPre(second,2,'0')).append("."); + sb.append(StrUtil.subSuf(temp,temp.length()-3) ); // 毫秒的小数部分 + // 返回字符串缓冲区的内容 + return sb.toString(); + } + + public static byte[] timeStringToCP56(String timeString) { + // 检查参数是否合法,如果不是符合时间格式的字符串,返回空数组 + if (timeString == null || !timeString.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}")) { + return new byte[0]; + } + // 定义一个字节数组,用来存储结果 + byte[] cp56 = new byte[7]; + // 使用正则表达式,将时间字符串分割为年、月、日、时、分、秒和毫秒,注意年份要减去2000,毫秒要乘以1000 + String[] timeUnits = timeString.split("[- :.]"); + int year = Integer.parseInt(timeUnits[0]) - 2000; + int month = Integer.parseInt(timeUnits[1]); + int day = Integer.parseInt(timeUnits[2]); + int hour = Integer.parseInt(timeUnits[3]); + int minute = Integer.parseInt(timeUnits[4]); + int millisecond =Integer.parseInt(timeUnits[5])* 1000 + Integer.parseInt(timeUnits[6]) ; + // 将每个时间单位转换为二进制数,并根据CP56格式的要求,存储到字节数组中 + // 注意要使用位运算符和位掩码,保证每个字节只有8位有效位 + cp56[0] = (byte) ((millisecond) ); // 毫秒的低8位 + cp56[1] = (byte) ((millisecond >> 8)); // 毫秒的高8位 + cp56[2] = (byte) (minute & 0xFF); // 分 + cp56[3] = (byte) (hour & 0xFF); // 时 + cp56[4] = (byte) (day & 0xFF); // 星期日和日 + cp56[5] = (byte) (month & 0xFF); // 月 + cp56[6] = (byte) (year & 0xFF); // 年 + // 返回字节数组 + return cp56; + } + + +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpHandler.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpHandler.java new file mode 100644 index 0000000..7ddf412 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpHandler.java @@ -0,0 +1,244 @@ +package com.yfd.platform.component.nettyudpserver; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.HexUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.service.ILinkageSignalService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.RtspToMP4; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.socket.DatagramPacket; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @create 2021/9/9 10:12 + */ +@Slf4j +@Component +public class NettyUdpHandler extends SimpleChannelInboundHandler { + + private static final Logger logger = LoggerFactory.getLogger(NettyUdpServer.class); + private static final Charset CHARSET = Charset.forName("GB2312"); + private ByteBuffer buffer; + + public static NettyUdpHandler nettyServerHandler; + + @Resource + private IStationRobotService stationRobotService; + @Resource + private ILinkageSignalService linkageSignalService; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private RtspToMP4 rtspToMP4; + @Resource + private ISubstationDeviceService substationDeviceService; + + @PostConstruct + public void init() { + nettyServerHandler = this; + nettyServerHandler.stationRobotService = this.stationRobotService; + nettyServerHandler.linkageSignalService = this.linkageSignalService; + nettyServerHandler.httpRESTfulUtils = this.httpRESTfulUtils; + nettyServerHandler.rtspToMP4 = this.rtspToMP4; + nettyServerHandler.substationDeviceService = this.substationDeviceService; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { + logger.info("收到主辅联动信号" + ctx.toString()); + // 获取消息内容 + MyMessageProtocol msg = new MyMessageProtocol(); + ByteBuf buf = packet.content(); + byte[] startflag = new byte[5]; + buf.readBytes(startflag); + String frametype = new String(HexUtil.encodeHex(startflag, false)); + logger.info("收到主辅联动信号" + frametype); + + //5590EB90EB + if (frametype.equals("EB90EB9055")) { + //接收到主辅设备联动控制信号 + log.info("接收到主辅设备联动控制信号"); + msg.setFrametype(frametype); +// msg.setStationnum(buf.readShort()); +// msg.setMonitorindex(buf.readShort()); + msg.setStationnum(buf.readShortLE()); + msg.setMonitorindex(buf.readShortLE()); + log.info("监控索引号:" + msg.getMonitorindex() + "监控类型:" + msg.getMonitortype() + "站序号:" + msg.getStationnum() + "和校验:" + msg.getSumverify()); + LinkageSignal one = + nettyServerHandler.linkageSignalService.getOne(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, msg.getMonitorindex())); + String linkageType = one.getLinkageType(); + String linkageDeviceType = one.getLinkageDeviceType(); + if ("1".equals(linkageType) && "遥信".equals(linkageDeviceType)) { + String deviceIdList = one.getDeviceIdList(); + List split = StrUtil.split(deviceIdList, ","); + int min = Math.min(split.size(), 3); + for (int i = 0; i < min; i++) { + int finalI = i; + new Thread(() -> { + String id = split.get(finalI); + SubstationDevice substationDevice = nettyServerHandler.substationDeviceService.getById(id); + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + if (jsonArray.size() > 0) { + JSONObject jsonObject = jsonArray.getJSONObject(0); + String patroldeviceCode = jsonObject.getStr("patroldevice_code"); + String patroldeviceChannelcode = jsonObject.getStr("patroldevice_channelcode"); + nettyServerHandler.rtspToMP4.stopRecord(patroldeviceCode + "_" + patroldeviceChannelcode); + } + }).start(); + + } + // String id = StrUtil.subBefore(deviceIdList, ",", false); + return; + } + msg.setMonitortype(buf.readByte()); + msg.setAttributelength(buf.readByte() & 0xFF); + // 读取属性描述 + byte[] firstBytes = new byte[msg.getAttributelength()]; + buf.readBytes(firstBytes); + String firstString = new String(firstBytes, "GB2312"); + msg.setAttribute(firstString); + + msg.setValuelength(buf.readByte() & 0xFF); + byte[] secondBytes = new byte[msg.getValuelength()]; + buf.readBytes(secondBytes); + String secondString = new String(secondBytes, "GB2312"); + msg.setValue(secondString); + + byte[] eventtimes = new byte[7]; + buf.readBytes(eventtimes); + String eventtime = MyMessageProtocol.cp56ToTimeString(eventtimes); + msg.setEventtime(eventtime); + +// short verifylength = buf.readShort(); +// Short resumlength = Short.parseShort(String.valueOf(14 + msg.getAttributelength() + msg.getValuelength())); +// +// msg.setSumverify(verifylength); +// if (verifylength != resumlength) { +// throw new Exception("传输内容长度校验不一致!"); +// } + log.info("创建一键顺控任务"); + log.info("msg" + msg); + //调用一键顺控方法,启动顺控任务 + nettyServerHandler.linkageSignalService.createLinkSiginTask(msg); + + //4390EB90EB + } else if (frametype.equals("EB90EB9043")) {//传输联动信号配置文件 + short stationcode = buf.readShort(); //站序号 + byte nextflag = buf.readByte(); //后续标志位 + log.info("后续标志位" + nextflag); + short frameno = buf.readShort(); //帧序号 + log.info("帧序号" + frameno); + int startlocation = buf.readInt(); //起始传输位置 + int framelength = buf.readByte() & 0xFF; //数据长度 + log.info("数据长度" + framelength); + // 读取本帧数据内容 + byte[] contentBytes = new byte[framelength]; + if (frameno == 0) { + buffer = ByteBuffer.allocate(100000000); + } + buf.readBytes(contentBytes); + buffer.put(contentBytes); + + int verifylength = buf.readByte() & 0xFF; + int resumlength = 10 + contentBytes.length; + log.info("verifylength:" + verifylength + "resumlength:" + resumlength); +// if (verifylength != resumlength) { +// throw new Exception("传输内容长度校验不一致!"); +// } + + if (nextflag == 0) { + //所有帧都读取完成 + log.info("所有帧都读取完成"); + buffer.flip(); + byte[] result = new byte[buffer.remaining()]; + buffer.get(result); + String filecontent = new String(result, "GB2312"); + log.info(filecontent); + // 调用后处理方法,将结果文件解析为联动信号对象数据,并保存到数据库中, + List linkageSignalList = analysisFileContent(filecontent); + List list = nettyServerHandler.linkageSignalService.list(new LambdaQueryWrapper().select(LinkageSignal::getControlNum)); + Set collect = list.stream().map(LinkageSignal::getControlNum).collect(Collectors.toSet()); + List dataList = linkageSignalList.stream().filter(l -> !collect.contains(l.getControlNum())).collect(Collectors.toList()); + if(dataList.size()>0){ + nettyServerHandler.linkageSignalService.saveBatch(dataList); + } + //todo 将联动配置文件上传到巡视主机上 + } + } + } + + /********************************** + * 用途说明: 解析数据内容到联动信号数据库 + * 参数说明 filecontent + * 返回值说明: java.util.List + ***********************************/ + private List analysisFileContent(String filecontent) { + List linkageSignalList = new ArrayList<>(); + String regex = "\\d{4}[-]\\d{2}[-]\\d{2} \\d{2}:\\d{2}:\\d{2}"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher1 = pattern.matcher(filecontent); + LocalDateTime now = LocalDateTime.now(); + if (matcher1.find()) { + String group = matcher1.group(); + now = LocalDateTimeUtil.parse(group, "yyyy-MM-dd hh:mm:ss"); + } + String regStr = "#[^\n]*\n"; + Pattern compile = Pattern.compile(regStr); + Matcher matcher = compile.matcher(filecontent); + while (matcher.find()) { + LinkageSignal linkageSignal = new LinkageSignal(); + String group = matcher.group(); + String[] split = group.split("\t"); + // 站序号 + linkageSignal.setStationNum(split[1]); + // 监控索引号 + linkageSignal.setControlNum(split[2]); + // 设备名称 + linkageSignal.setLinkageDeviceName(split[3]); + // 设备类型 + linkageSignal.setLinkageDeviceType(split[4]); + // 实物ID + linkageSignal.setMaterialId(split[5]); + // 是否联动信号 + linkageSignal.setIsLinkageFlag(split[6]); + linkageSignal.setTime(now); + linkageSignal.setDatastatus("0"); + linkageSignal.setIsenable("0"); + linkageSignal.setLastmodifydate(LocalDateTime.now()); + linkageSignal.setLastmodifier("主辅联动"); + linkageSignalList.add(linkageSignal); + } + return linkageSignalList; + } +} + + + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpRunner.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpRunner.java new file mode 100644 index 0000000..31c86fc --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpRunner.java @@ -0,0 +1,39 @@ +package com.yfd.platform.component.nettyudpserver; + + +import io.netty.channel.Channel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +@Component +@Order(value = 20) +public class NettyUdpRunner implements CommandLineRunner { + private static final Logger logger = LoggerFactory.getLogger(NettyUdpRunner.class); + + @Override + public void run(String... args) throws Exception { + + new Thread(() -> { + NettyUdpServer server = null; + try { + server = new NettyUdpServer(9300); + Channel channel = server.bind(); + logger.info("NettyUdpRunner running"); + channel.closeFuture().sync(); + } catch (Exception e) { + logger.error(e.getMessage());; + } finally { + if(server!=null){ + server.stop(); + } + } + } + ).start(); + } + + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpServer.java b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpServer.java new file mode 100644 index 0000000..a83f879 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/nettyudpserver/NettyUdpServer.java @@ -0,0 +1,63 @@ +package com.yfd.platform.component.nettyudpserver; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioDatagramChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; + +/** + * netty服务器,主要用于与客户端通讯 + */ + +public class NettyUdpServer { + private static final Logger logger = LoggerFactory.getLogger(NettyUdpServer.class); + private static final int MAX_FRAME_LENGTH = 1024 * 1024; //最大长度 + private static final int LENGTH_FIELD_LENGTH = 1; //长度字段所占的字节数 + private static final int LENGTH_FIELD_OFFSET = 0; //长度偏移 + private static final int LENGTH_ADJUSTMENT = 0; + private static final int INITIAL_BYTES_TO_STRIP = 1; + + private final EventLoopGroup group; + private final Bootstrap bootstrap; + + public NettyUdpServer(int port) throws Exception { + group = new NioEventLoopGroup(); + bootstrap = new Bootstrap(); + bootstrap.group(group) + .channel(NioDatagramChannel.class) + .option(ChannelOption.SO_BROADCAST,true) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + ChannelPipeline pipeline = channel.pipeline(); + //加入自己的业务处理handler + pipeline.addLast(new NettyUdpHandler()); + } + }).localAddress(new InetSocketAddress(port)); + logger.info("Netty Udp server is started!(port:" + port + ")"); + } + + public Channel bind(){ + return bootstrap.bind().syncUninterruptibly().channel(); + } + + public void stop(){ + group.shutdownGracefully(); + } + + public static void main(String[] args) throws Exception{ + NettyUdpServer server = new NettyUdpServer(9300); + try { + Channel channel = server.bind(); + logger.info("NettyUdpServer running"); + channel.closeFuture().sync(); + }finally { + server.stop(); + } + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/component/sntp/SNTPClient.java b/riis-system/src/main/java/com/yfd/platform/component/sntp/SNTPClient.java new file mode 100644 index 0000000..f1847e4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/sntp/SNTPClient.java @@ -0,0 +1,139 @@ +package com.yfd.platform.component.sntp; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson2.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.net.ntp.NTPUDPClient; +import org.apache.commons.net.ntp.TimeInfo; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.InetAddress; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; +import java.util.Date; +import java.util.Locale; + +//@Component +@Slf4j +public class SNTPClient { + + @Value("${sntp.server-ip}") + private String server_ip; + @Value("${sntp.server-port}") + private int server_port; + + public void syncServerTime(String serverAddress, int serverPort) { + //String host = "ntp.aliyun.com"; // 可以替换为具体的NTP服务器地址 + NTPUDPClient client = new NTPUDPClient(); // 创建SNTP客户端 + client.setDefaultTimeout(5000); // 设置超时时间为5秒 + try { + client.open(); + // 获取阿里云公网NTP服务器地址 + InetAddress hostAddr = InetAddress.getByName(serverAddress); + TimeInfo info = client.getTime(hostAddr, serverPort); + info.computeDetails(); // 计算时间差 + // 获取网络时间 + long serverTime = System.currentTimeMillis() + info.getOffset(); + Date currentdate = new Date(serverTime); + // 设置系统时间 + Date newTime = new Date(serverTime); + setSystemTime(newTime); + } catch (IOException e) { + log.error(e.getMessage());; + throw new RuntimeException("同步失败"); + } finally { + client.close(); + } + } + + // @Scheduled(initialDelay = 20000, fixedRate = 10000) + //@Scheduled(fixedRate = 60000) + public void syncTimeTask() { + SNTPClient sntpClient = new SNTPClient(); + //sntpClient.syncServerTime("time1.aliyun.com", server_port); + sntpClient.syncServerTime(server_ip, server_port); + } + + public JSONObject getCurrentSystemTime() { + // 获取当前日期 + LocalDate currentDate = LocalDate.now(); + + // 获取年份 + int year; // 注意:从Java 9开始,应该使用 currentDate.getYearValue() + + // 但在Java 8及以后版本中,应该使用以下方式 + year = currentDate.getYear(); + + // 获取月份(月份从1开始计数,不是从0) + int month = currentDate.getMonthValue(); + + // 获取日期(日) + int dayOfMonth = currentDate.getDayOfMonth(); + + String monthStr = month > 9 ? month + "" : "0" + month; + String dayStr = dayOfMonth > 9 ? dayOfMonth + "" : "0" + dayOfMonth; + + // 获取星期几 + DayOfWeek dayOfWeek = currentDate.getDayOfWeek(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("year", year); + jsonObject.put("month", monthStr); + jsonObject.put("dayOfMonth", dayStr); + jsonObject.put("dayOfWeek", dayOfWeek.getValue()); + jsonObject.put("weekName", dayOfWeek.getDisplayName(TextStyle.FULL, Locale.getDefault())); + return jsonObject; + + } + + public static void setSystemTime(Date newTime) { + try { + //时间转换 + Instant instant = newTime.toInstant(); // 将java.util.Date转换为java.time.Instant + LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime(); // 转换为本地日期时间 + //linux修改时间 + String timeStr = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + //windows修改时间年-月-日 + String setDateCommand = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + //windows修改时间时-分-秒 + String setDateProcess = localDateTime.format(DateTimeFormatter.ofPattern("HH:mm:ss")); + + if (isWindows()) { + // 使用Windows系统命令设置时间 + execCmd("cmd /c date " + setDateCommand); + execCmd("cmd /c time " + setDateProcess); + } else if (isLinux()) { + // 使用Linux系统命令设置时间 + execCmd("sudo date -s \"" + timeStr + "\""); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + } + + public static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } + + public static boolean isLinux() { + return System.getProperty("os.name").toLowerCase().contains("linux"); + } + + public static void execCmd(String command) { + try { + Process process = Runtime.getRuntime().exec(command); + int exitCode = process.waitFor(); + if (exitCode == 0) { + log.info("系统时间修改成功!"); + } else { + log.error("系统时间修改失败!"); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + } +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/component/sntp/SNTPServer.java b/riis-system/src/main/java/com/yfd/platform/component/sntp/SNTPServer.java new file mode 100644 index 0000000..914d6d1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/component/sntp/SNTPServer.java @@ -0,0 +1,70 @@ +package com.yfd.platform.component.sntp; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.*; +import java.util.Date; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +//@Component +@RequiredArgsConstructor +@Slf4j +public class SNTPServer implements ApplicationRunner { + + @Override + @Async + public void run(ApplicationArguments applicationArguments) { + int port = 123; // SNTP server port + + try (DatagramSocket serverSocket = new DatagramSocket(port)) { + log.info("SNTP Server is running on port " + port); + + while (true) { + byte[] receiveData = new byte[48]; + DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); + serverSocket.receive(receivePacket); + + long currentTimeMillis = System.currentTimeMillis(); + // 获取当前时间信息 + long currentTime = System.currentTimeMillis(); + long ntpTime = currentTime + 2208988800000L; // Unix时间戳起始时间为1900年,Java起始时间为1970年 + ntpTime -= 2000000000; + // 构造NTP响应数据包 + byte[] buffer = new byte[48]; + buffer[0] = (byte) 0x1B; // 设置版本号和模式 + for (int i = 1; i < 48; i++) { + buffer[i] = (byte) 0; // 设置其他字段为0 + } + long seconds = ntpTime / 1000; + long fraction = (long) ((ntpTime % 1000) * (1L << 32) / 1000); + buffer[40] = (byte) ((seconds >> 24) & 0xFF); + buffer[41] = (byte) ((seconds >> 16) & 0xFF); + buffer[42] = (byte) ((seconds >> 8) & 0xFF); + buffer[43] = (byte) (seconds & 0xFF); + buffer[44] = (byte) ((fraction >> 24) & 0xFF); + buffer[45] = (byte) ((fraction >> 16) & 0xFF); + buffer[46] = (byte) ((fraction >> 8) & 0xFF); + buffer[47] = (byte) (fraction & 0xFF); + + InetAddress clientAddress = receivePacket.getAddress(); + int clientPort = receivePacket.getPort(); + DatagramPacket sendPacket = new DatagramPacket(buffer, buffer.length, clientAddress, clientPort); + serverSocket.send(sendPacket); + log.info("Sent current time to client: " + new Date(currentTimeMillis)); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + } + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/config/AlarmTemplateConfig.java b/riis-system/src/main/java/com/yfd/platform/config/AlarmTemplateConfig.java new file mode 100644 index 0000000..42c3ed8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/AlarmTemplateConfig.java @@ -0,0 +1,60 @@ +package com.yfd.platform.config; + +import org.junit.jupiter.api.Order; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * + * @Date: 2023/6/12 17:19 + * @Description: + */ +@Configuration("AlarmTemplateConfig") +@Order(0) +public class AlarmTemplateConfig { + + @Value("${alarmtemplate.helmetcamera}") + private String helmetCamera; + + @Value("${alarmtemplate.clothcamera}") + private String clothCamera; + + @Value("${alarmtemplate.intensivecamera}") + private String intensiveCamera; + + @Value("${alarmtemplate.firecamera}") + private String fireCamera; + + @Value("${alarmtemplate.leakagecamera}") + private String leakageCamera; + + @Value("${alarmtemplate.person_enter}") + private String personEnter; + + @Value("${alarmtemplate.person_smoke}") + private String personSmoke; + + public String getHelmetCamera() { + return helmetCamera; + } + + public String getClothCamera() { + return clothCamera; + } + + public String getIntensiveCamera() { + return intensiveCamera; + } + + public String getFireCamera() { + return fireCamera; + } + + public String getLeakageCamera() { + return leakageCamera; + } + + public String getpersonEnter() { return personEnter; } + + public String getpersonSmoke() { return personSmoke; } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/AlgorithmManufacturerConfig.java b/riis-system/src/main/java/com/yfd/platform/config/AlgorithmManufacturerConfig.java new file mode 100644 index 0000000..f779460 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/AlgorithmManufacturerConfig.java @@ -0,0 +1,39 @@ +package com.yfd.platform.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Date: 2024/7/24 11:26 + * @Description: + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "algorithm") +public class AlgorithmManufacturerConfig { + + private List> manufacturer; + + // getters and setters + public List> getManufacturer() { + return this.manufacturer; + } + + public void setManufacturer(List> manufacturer) { + this.manufacturer = manufacturer; + } + + public Map getManufacturerName() { + + Map map1 = new HashMap<>(); + for (Map map : this.manufacturer) { + map1.put(map.get("id"), map.get("name")); + } + return map1; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/AlgorithmServerConfig.java b/riis-system/src/main/java/com/yfd/platform/config/AlgorithmServerConfig.java new file mode 100644 index 0000000..c41c0af --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/AlgorithmServerConfig.java @@ -0,0 +1,65 @@ +package com.yfd.platform.config; + +import org.junit.jupiter.api.Order; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * + * @Date: 2023/7/25 15:41 + * @Description: + */ +@Configuration("algorithmServerConfig") +@Order(0) +public class AlgorithmServerConfig { + + @Value("${algorithmserver.serverip}") + private String serverIp; + + @Value("${algorithmserver.ftpport}") + private String ftpPort; + + @Value("${algorithmserver.ftpusername}") + private String ftpUsername; + + @Value("${algorithmserver.ftppassword}") + private String ftpPassword; + + @Value("${algorithmserver.httpport}") + private String httpPort; + + @Value("${algorithmserver.username}") + private String username; + + @Value("${algorithmserver.password}") + private String password; + + public String getServerIp() { + return serverIp; + } + + public String getFtpPort() { + return ftpPort; + } + + public String getFtpUsername() { + return ftpUsername; + } + + public String getFtpPassword() { + return ftpPassword; + } + + public String getHttpPort() { + return httpPort; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/DatabaseConfigReader.java b/riis-system/src/main/java/com/yfd/platform/config/DatabaseConfigReader.java new file mode 100644 index 0000000..7b01b2c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/DatabaseConfigReader.java @@ -0,0 +1,27 @@ +package com.yfd.platform.config; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +public class DatabaseConfigReader { + + @Autowired + private Environment environment; + + public String getDatabaseUrl() { + return environment.getProperty("spring.datasource.druid.master.url"); + } + + public String getDatabaseUsername() { + return environment.getProperty("spring.datasource.druid.master.username"); + } + + public String getDatabasePassword() { + return environment.getProperty("spring.datasource.druid.master.password"); + } + + public String getBackupPath() { + return environment.getProperty("spring.datasource.druid.master.backuppath"); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/DynamicTask.java b/riis-system/src/main/java/com/yfd/platform/config/DynamicTask.java new file mode 100644 index 0000000..24d97f1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/DynamicTask.java @@ -0,0 +1,202 @@ +package com.yfd.platform.config; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.system.domain.SysConfig; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysConfigService; +import com.yfd.platform.system.service.ISysRoleService; +import com.yfd.platform.system.service.IUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.io.File; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 动态定时任务 + * :用于延时执行任务 或者 重试执行任务 + */ +@Component +public class DynamicTask { + + private final Logger logger = LoggerFactory.getLogger(DynamicTask.class); + + private ThreadPoolTaskScheduler threadPoolTaskScheduler; + + private final Map> futureMap = new ConcurrentHashMap<>(); + private final Map runnableMap = new ConcurrentHashMap<>(); + + @Resource + private DatabaseConfigReader databaseConfigReader; + + @Resource + private IUserService userService; + +// @Resource +// private JavaMailSender mailSender; + @Resource + private ISysConfigService sysConfigService; +// @Value("${spring.mail.username}") +// private String sendMailer; + + @PostConstruct + public void DynamicTask() { + threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(300); + threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true); + threadPoolTaskScheduler.setAwaitTerminationSeconds(10); + threadPoolTaskScheduler.initialize(); + } + + /** + * 循环执行的任务 + * @param key 任务ID + * @param task 任务 + * @param cycleForCatalog 间隔 毫秒 + * @return + */ + public void startCron(String key, Runnable task, int cycleForCatalog) { + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } + // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 + future = threadPoolTaskScheduler.scheduleAtFixedRate(task, cycleForCatalog); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); + } + } + + /** + * 延时任务 + * @param key 任务ID + * @param task 任务 + * @param delay 延时 /毫秒 + * @return + */ + public void startDelay(String key, Runnable task, int delay) { + stop(key); + + // 获取执行的时刻 + Instant startInstant = Instant.now().plusMillis(TimeUnit.MILLISECONDS.toMillis(delay)); + + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } + future = threadPoolTaskScheduler.schedule(task, startInstant); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); + } + } + + public boolean stop(String key) { + boolean result = false; + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) { + result = futureMap.get(key).cancel(false); + futureMap.remove(key); + runnableMap.remove(key); + } + return result; + } + + public boolean contains(String key) { + return futureMap.get(key) != null; + } + + public Set getAllKeys() { + return futureMap.keySet(); + } + + public Runnable get(String key) { + return runnableMap.get(key); + } + + /** + * 每五分钟检查失效的任务,并移除 + */ + @Scheduled(cron="0 0/5 * * * ?") + public void execute(){ + if (futureMap.size() > 0) { + for (String key : futureMap.keySet()) { + if (futureMap.get(key).isDone() || futureMap.get(key).isCancelled()) { + futureMap.remove(key); + runnableMap.remove(key); + } + } + } + } + + public boolean isAlive(String key) { + return futureMap.get(key) != null && !futureMap.get(key).isDone() && !futureMap.get(key).isCancelled(); + } + + /** + * 检查日志文件大小是否告警(邮箱告警) + * 每天凌晨执行 0 0 0 1/1 * ? * + */ +// @Scheduled(cron = "0 0 0 1/1 * ?") +// public void checkLog() { +// SysConfig sysConfig = sysConfigService.getOne(null); +// // 日志容量 (单位GB) +// String custom3 = sysConfig.getCustom3(); +// if (StrUtil.isBlank(custom3)) { +// return; +// } +// double number = NumberUtil.parseDouble(custom3) * 0.9; +// long size = FileUtil.size(new File(databaseConfigReader.getBackupPath() + "sysLog.txt")); +// size = size / (1024 * 1024 * 1024); +// if (size < number) { +// return; +// } +// List userList = userService.getAuditManagement(); +// List emailList = userList.stream().map(SysUser::getEmail).collect(Collectors.toList()); +// for (String email : emailList) { +// //发送容量告警 +// SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); +// simpleMailMessage.setFrom(sendMailer); +// simpleMailMessage.setTo(email); +// simpleMailMessage.setSubject("日志容量告警"); +// simpleMailMessage.setText("审计日志容量达【审计日志容量上限】的90%"); +// mailSender.send(simpleMailMessage); +// } +// } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/FileProperties.java b/riis-system/src/main/java/com/yfd/platform/config/FileProperties.java new file mode 100644 index 0000000..2203a7f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/FileProperties.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + + +@Data +@Configuration +@ConfigurationProperties(prefix = "file") +public class FileProperties { + + /** 文件大小限制 */ + private Long maxSize; + + /** 头像大小限制 */ + private Long avatarMaxSize; + + private ElPath mac; + + private ElPath linux; + + private ElPath windows; + + public ElPath getPath(){ + String os = System.getProperty("os.name"); + if(os.toLowerCase().startsWith("win")) { + return windows; + } else if(os.toLowerCase().startsWith("mac")){ + return mac; + } + return linux; + } + + @Data + public static class ElPath{ + + private String path; + + private String avatar; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/GlobalExceptionHandler.java b/riis-system/src/main/java/com/yfd/platform/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..8096855 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/GlobalExceptionHandler.java @@ -0,0 +1,26 @@ +package com.yfd.platform.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * + * @Date: 2023/3/27 18:07 + * @Description: + */ +@Slf4j +@ControllerAdvice +public class GlobalExceptionHandler { + + @ResponseBody + @ExceptionHandler(value = Throwable.class) + public ResponseResult handleException(Throwable e) { + log.error(e.getMessage());; + String message = e.getMessage(); + log.error("message:{}", message); + return ResponseResult.error(e.getMessage()); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/HttpServerConfig.java b/riis-system/src/main/java/com/yfd/platform/config/HttpServerConfig.java new file mode 100644 index 0000000..0d904ab --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/HttpServerConfig.java @@ -0,0 +1,281 @@ +package com.yfd.platform.config; + +import org.junit.jupiter.api.Order; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; +import java.util.regex.Pattern; + + +@Configuration("httpServerConfig") +@Order(0) +public class HttpServerConfig { + + @Value("${httpserver.monitorserver.ip}") + private String monitorIp; + + @Value("${httpserver.monitorserver.port}") + private String monitorPort; + @Value("${httpserver.monitorserver.rtspport}") + private String monitorRtspPort; + + @Value("${httpserver.monitorserver.appname}") + private String monitorAppname; + + @Value("${httpserver.meidiaserver.ip}") + private String mediaIp; + + @Value("${httpserver.meidiaserver.port}") + private String mediaPort; + + @Value("${httpserver.meidiaserver.secret}") + private String mediaSecret; + + + + @Value("${httpserver.analyseserver.ip}") + private String analyseIp; + + @Value("${httpserver.analyseserver.port11}") + private String analysePort11; + + @Value("${httpserver.analyseserver.port12}") + private String analysePort12; + + @Value("${httpserver.analyseserver.port13}") + private String analysePort13; + @Value("${httpserver.analyseserver.callanalyse}") + private String callAnalyse; + + @Value("${httpserver.sdkserver.ip}") + private String sdkIp; + + @Value("${httpserver.sdkserver.haikangport}") + private String haikangPort; + @Value("${httpserver.sdkserver.dahuaport}") + private String dahuaPort; + @Value("${httpserver.sdkserver.heikaport}") + private String heikaPort; + + @Value("${httpserver.sdkserver.haikangapp}") + private String haikangApp; + @Value("${httpserver.sdkserver.heikaapp}") + private String heikaApp; + @Value("${httpserver.sdkserver.dahuaapp}") + private String dahuaApp; + + @Value("${httpserver.dockserver.ip}") + private String dockServerIp; + @Value("${httpserver.dockserver.port}") + private String dockServerPort; + @Value("${httpserver.dockserver.app}") + private String dockServerApp; + + + @Value("${httpserver.patrolserver.ip}") + private String patrolIp; + + @Value("${httpserver.patrolserver.httpport}") + private String patrolHttpPort; + + @Value("${httpserver.patrolserver.tcpport}") + private String patrolTcpPort; + + + @Value("${httpserver.patrolserver.udpport}") + private String patrolUdpPort; + + @Value("${httpserver.patrolserver.serverid}") + private String patrolServerid; + + @Value("${httpserver.patrolserver.robotid}") + private String robotId; + + @Value("${httpserver.patrolserver.appname}") + private String patrolAppname; + + //是否执行定时任务 + @Value("${httpserver.patrolserver.dotask}") + private String doTask; + + @Value("${httpserver.patrolserver.snapfilepath}") + private String snapFilePath; + + @Value("${httpserver.patrolserver.alarmfilepath}") + private String alarmFilePath; + + @Value("${httpserver.patrolserver.modelpath}") + private String modelPath; + + @Value("${httpserver.patrolserver.documentpath}") + private String documentPath; + + @Value("${httpserver.patrolserver.tempfilepath}") + private String tempFilePath; + + @Value("${httpserver.patrolserver.ffmpegpath}") + private String ffmpegPath; + + @Value("${httpserver.patrolserver.callmethod}") + private String callMethod; + + @Value("${httpserver.patrolserver.filefromtype}") + private String filefromtype; + + @Value("${httpserver.patrolserver.testfilepath}") + private String testfilepath; + + public String getMonitorIp() { + return monitorIp; + } + + public String getMediaIp() { + return mediaIp; + } + + public String getMonitorPort() { + return monitorPort; + } + + public String getMonitorRtspPort() { + return monitorRtspPort; + } + public String getMonitorAppname() { + return monitorAppname; + } + + public String getAnalyseIp() { + return analyseIp; + } + + public String getAnalysePort11() { + return analysePort11; + } + + public String getAnalysePort12() { + return analysePort12; + } + + public String getAnalysePort13() { + return analysePort13; + } + + public String getCallAnalyse() { + return callAnalyse; + } + + public String getSdkIp() { + return sdkIp; + } + + public String getHaikangApp() { + return haikangApp; + } + public String getDahuaApp() { + return dahuaApp; + } + public String getHeikaApp() { + return heikaApp; + } + + + public String getHaikangPort() { + return haikangPort; + } + public String getDahuaPort() { + return dahuaPort; + } + public String getHeikaPort() { + return heikaPort; + } + + public String getDockServerIp() { + return dockServerIp; + } + + public String getDockServerPort() { + return dockServerPort; + } + + public String getDockServerApp() { + return dockServerApp; + } + + public String getPatrolIp() { + return patrolIp; + } + + public String getPatrolPort() { + return patrolHttpPort; + } + + public String getPatrolAppname() { + return patrolAppname; + } + + public String getPatrolServerid() { + return patrolServerid; + } + + public String getRobotId() { + return robotId; + } + + public String getPatrolUdpPort() { + return patrolUdpPort; + } + + public String getffmpegPath() { + return ffmpegPath; + } + + + public String getPatrolTcpPort() { + return patrolTcpPort; + } + + public String getDoTask() { + return doTask; + } + + public String getSnapFilePath() { + return snapFilePath; + } + + public String getAlarmFilePath() { + return alarmFilePath; + } + + public String getModelPath() { + return modelPath; + } + + public String getDocumentPath() { + return documentPath; + } + + public String getTempFilePath() { + return tempFilePath; + } + + public String getCallMethod() { + return callMethod; + } + + public String getFilefromtype() { + return filefromtype; + } + + public String getTestfilepath() { + return testfilepath; + } + + private boolean isValidIPAddress(String ipAddress) { + if ((ipAddress != null) && (!ipAddress.isEmpty())) { + return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress); + } + return false; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/JobRunner.java b/riis-system/src/main/java/com/yfd/platform/config/JobRunner.java new file mode 100644 index 0000000..a0b1b88 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/JobRunner.java @@ -0,0 +1,101 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.impl.SubstationPatroldeviceServiceImpl; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.impl.QuartzMultiTaskManage; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.mapper.QuartzJobMapper; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.QuartzManage; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @date 2019-01-07 + */ +@Component +@RequiredArgsConstructor +public class JobRunner implements ApplicationRunner { + + private static final Logger log = LoggerFactory.getLogger(JobRunner.class); + private final QuartzJobMapper quartzJobMapper; + private final TaskTodoMapper taskTodoMapper; + private final QuartzManage quartzManage; + private final QuartzMultiTaskManage quartzTaskManage; + private final SubstationPatroldeviceServiceImpl patroldeviceService; + private final RedisTemplate redisTemplate; + private final HttpRESTfulUtils httpUtil; + private final HttpServerConfig config; + private final IPlatformParentSystemService platformParentSystemService; + + /** + * 项目启动时重新激活启用的定时任务 + * + * @param applicationArguments / + */ + @Override + public void run(ApplicationArguments applicationArguments) { + + log.info("--------------------注入系统定时任务---------------------"); + List quartzJobs = + quartzJobMapper.selectList(new LambdaQueryWrapper().eq(QuartzJob::getStatus, "1")); + quartzJobs.forEach(quartzManage::addJob); + log.info("--------------------系统任务注入完成---------------------"); + + log.info("--------------------注入变电站定时巡视任务---------------------"); + List todoTaskJobs = taskTodoMapper.selectTaskTodoList(); + todoTaskJobs.forEach(quartzTaskManage::addJob); + log.info("--------------------变电站定时巡视任务注入完成---------------------"); + + log.info("--------------------注入变电站摄像机静默巡视任务---------------------"); + // patroldeviceService.addDailyMonitorTask(); + log.info("--------------------变电站摄像机静默巡视任务注入完成---------------------"); + + log.info("--------------------自启动上级系统注册---------------------"); + List list = + platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + if (list != null && list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + platformParentSystemService.sendRegisterToSuper(platformParentSystem.getId()); + } + + log.info("--------------------自启动上级系统注册完成---------------------"); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/JwtAuthenticationTokenFilter.java b/riis-system/src/main/java/com/yfd/platform/config/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..fc709e0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/JwtAuthenticationTokenFilter.java @@ -0,0 +1,97 @@ +package com.yfd.platform.config; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.jwt.JWT; +import cn.hutool.jwt.JWTUtil; +import com.alibaba.fastjson.JSON; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.utils.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + + @Autowired + private WebConfig webConfig; + + @Override + protected void doFilterInternal(HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + FilterChain filterChain) throws ServletException, IOException { + String referer = httpServletRequest.getHeader("referer"); + // String serverName = httpServletRequest.getServerName(); + String serverName = StringUtils.getIpAddr(httpServletRequest); + // 是否开启referer验证 + boolean isOpen = false; + if (isOpen && null != referer && !referer.contains(serverName)) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("code", "1"); + jsonObject.putOnce("msg", "系统不支持当前域名的访问"); + httpServletResponse.setContentType("application/json; charset=utf-8"); + httpServletResponse.getWriter().write(jsonObject.toString()); + return; + } + //获取token + String uri = httpServletRequest.getRequestURI(); + String token = httpServletRequest.getHeader("token"); + if (StrUtil.isEmpty(token) || "/user/login".equals(uri)) { + filterChain.doFilter(httpServletRequest, httpServletResponse); + return; + } + //解析token + boolean isok = JWTUtil.verify(token, "12345678".getBytes()); + String userid = ""; + if (isok) { + final JWT jwt = JWTUtil.parseToken(token); + userid = jwt.getPayload("userid").toString(); + //从cachekey中获取用户信息失效时间 + String cachekey = "expire_time:" + userid; + if (StrUtil.isNotEmpty(webConfig.loginuserCache().get(cachekey)) && false) { + long expire_time = Long.parseLong(webConfig.loginuserCache().get(cachekey)); + if (System.currentTimeMillis() > expire_time) { + // webConfig.loginuserCache().remove("login:" + userid); + httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Token超过期限!"); + return; + } + } + } + + //从cachekey中获取用户信息 + String cachekey = "login:" + userid; + String jsonstr = webConfig.loginuserCache().get(cachekey); + LoginUser loginUser = JSON.parseObject(jsonstr, LoginUser.class); + if (ObjectUtil.isEmpty(loginUser)) { + httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, + "登录用户已失效!"); + return; + } + + + //存入SecurityContextHolder + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(loginUser, null, + loginUser.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + webConfig.loginuserCache().put(Constant.TOKEN + userid, token); + //更新了超期时间 + long expireTime = System.currentTimeMillis() + (30L * 60L * 1000L); + webConfig.loginuserCache().put("expire_time:" + userid, String.valueOf(expireTime)); + + //放行过滤器 + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/LikeStringEscapeInterceptor.java b/riis-system/src/main/java/com/yfd/platform/config/LikeStringEscapeInterceptor.java new file mode 100644 index 0000000..8b12930 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/LikeStringEscapeInterceptor.java @@ -0,0 +1,28 @@ +package com.yfd.platform.config; + +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import com.yfd.platform.utils.MybatisUtil; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.sql.SQLException; + +/****************************** + * 用途说明: mybatis-plus分页查询特殊字符处理 + * 作者姓名: pcj + * 创建时间: 2022/10/24 10:50 + ******************************/ +public class LikeStringEscapeInterceptor implements InnerInterceptor { + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, + ResultHandler resultHandler, BoundSql boundSql) throws SQLException { + // 为了在分页插件中复用,此处抽取出静态方法 + MybatisUtil.escapeParameterIfContainingLike(ms, boundSql); + InnerInterceptor.super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/LocalDateTimeSerializerConfig.java b/riis-system/src/main/java/com/yfd/platform/config/LocalDateTimeSerializerConfig.java new file mode 100644 index 0000000..500bec4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/LocalDateTimeSerializerConfig.java @@ -0,0 +1,29 @@ +package com.yfd.platform.config; + +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * + * @Date: 2023/9/4 14:08 + * @Description: LocalDateTime格式处理 + */ +@Configuration +public class LocalDateTimeSerializerConfig { + + @Bean + public LocalDateTimeSerializer localDateTimeDeserializer() { + return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Bean + public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { + return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer()); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/MessageConfig.java b/riis-system/src/main/java/com/yfd/platform/config/MessageConfig.java new file mode 100644 index 0000000..46b5fc2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/MessageConfig.java @@ -0,0 +1,45 @@ +package com.yfd.platform.config; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.IUserService; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * + * @Date: 2023/3/24 15:56 + * @Description: + */ +@Component +public class MessageConfig { + + @Resource + private IMessageService messageService; + + @Resource + private IUserService userService; + + @Resource + private WebConfig webConfig; + + public void sendMessage() { + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + String userId = userService.getUserInfo().getId(); + String token = webConfig.loginuserCache().get(Constant.TOKEN + userId); + ServerSendEventServer.sendMessage(token, count + ""); + } + + public void addMessage(Message message) { + messageService.save(message); + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + ServerSendEventServer.sendMessage(count + ""); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/MybitsPlusConfig.java b/riis-system/src/main/java/com/yfd/platform/config/MybitsPlusConfig.java new file mode 100644 index 0000000..8c41209 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/MybitsPlusConfig.java @@ -0,0 +1,34 @@ +package com.yfd.platform.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; +import com.baomidou.mybatisplus.extension.MybatisMapWrapperFactory; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/****************************** + * 用途说明: + * 作者姓名: pcj + * 创建时间: 2022/10/24 10:50 + ******************************/ +@Configuration +public class MybitsPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); + mybatisPlusInterceptor.addInnerInterceptor(new LikeStringEscapeInterceptor()); + mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + + return mybatisPlusInterceptor; + } + + @Bean + public ConfigurationCustomizer mybatisConfigurationCustomizer() { + return configuration -> configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory()); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/ParentConfig.java b/riis-system/src/main/java/com/yfd/platform/config/ParentConfig.java new file mode 100644 index 0000000..4940661 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/ParentConfig.java @@ -0,0 +1,97 @@ +package com.yfd.platform.config; + +import org.junit.jupiter.api.Order; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * @Date: 2023/7/25 15:41 + * @Description: + */ +@Configuration("ParentConfig") +@Order(0) +public class ParentConfig { + + @Value("${parentserver.serverip}") + private String parentIp; + + @Value("${parentserver.serverid}") + private String parentId; + + @Value("${parentserver.tcpport}") + private String tcpPort; + + @Value("${httpserver.patrolserver.serverid}") + private String serverId; + + private String heartBeatInterval = "60"; + private String patroldeviceRunInterval = "60"; + private String nestRunInterval = "60"; + private String weatherInterval = "60"; + + public String getParentIp() { + return parentIp; + } + + public String getParentId() { + return parentId; + } + + public String getServerId() { + return serverId; + } + + public String getTcpPort() { + return tcpPort; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public void setParentIp(String parentIp) { + this.parentIp = parentIp; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public void setTcpPort(String tcpPort) { + this.tcpPort = tcpPort; + } + + public String getHeartBeatInterval() { + return heartBeatInterval; + } + + public void setHeartBeatInterval(String heartBeatInterval) { + this.heartBeatInterval = heartBeatInterval; + } + + public String getPatroldeviceRunInterval() { + return patroldeviceRunInterval; + } + + public void setPatroldeviceRunInterval(String patroldeviceRunInterval) { + this.patroldeviceRunInterval = patroldeviceRunInterval; + } + + public String getNestRunInterval() { + return nestRunInterval; + } + + public void setNestRunInterval(String nestRunInterval) { + this.nestRunInterval = nestRunInterval; + } + + public String getWeatherInterval() { + return weatherInterval; + } + + public void setWeatherInterval(String weatherInterval) { + this.weatherInterval = weatherInterval; + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/PermissionConfig.java b/riis-system/src/main/java/com/yfd/platform/config/PermissionConfig.java new file mode 100644 index 0000000..28a8b1d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/PermissionConfig.java @@ -0,0 +1,25 @@ +package com.yfd.platform.config; + +import com.yfd.platform.utils.SecurityUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Service(value = "el") +public class PermissionConfig { + + public Boolean check(String... permissions) throws RuntimeException { + // 获取当前用户的所有权限 + List elPermissions = SecurityUtils.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()); + String currentUsername = SecurityUtils.getCurrentUsername(); + // 判断当前用户的所有权限是否包含接口上定义的权限 + boolean flag = currentUsername.equals("admin") || Arrays.stream(permissions).anyMatch(elPermissions::contains); + if (!flag) { + throw new RuntimeException("没有该权限"); + } + return flag; + } +} \ No newline at end of file diff --git a/riis-system/src/main/java/com/yfd/platform/config/QuartzConfig.java b/riis-system/src/main/java/com/yfd/platform/config/QuartzConfig.java new file mode 100644 index 0000000..a51dc3e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/QuartzConfig.java @@ -0,0 +1,74 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config; + +import org.quartz.Scheduler; +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.AdaptableJobFactory; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.stereotype.Component; + +/** + * 定时任务配置 + * + * @date 2019-01-07 + */ +@Configuration +public class QuartzConfig { + + /** + * 解决Job中注入Spring Bean为null的问题 + */ + @Component("quartzJobFactory") + public static class QuartzJobFactory extends AdaptableJobFactory { + + private final AutowireCapableBeanFactory capableBeanFactory; + + public QuartzJobFactory(AutowireCapableBeanFactory capableBeanFactory) { + this.capableBeanFactory = capableBeanFactory; + } + + @Override + protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { + + //调用父类的方法 + Object jobInstance = super.createJobInstance(bundle); + capableBeanFactory.autowireBean(jobInstance); + return jobInstance; + } + } + + /** + * 注入scheduler到spring + * + * @param quartzJobFactory / + * @return Scheduler + * @throws Exception / + */ + @Bean(name = "scheduler") + public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception { + SchedulerFactoryBean factoryBean = new SchedulerFactoryBean(); + factoryBean.setJobFactory(quartzJobFactory); + factoryBean.afterPropertiesSet(); + Scheduler scheduler = factoryBean.getScheduler(); + scheduler.start(); + return scheduler; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/ResponseResult.java b/riis-system/src/main/java/com/yfd/platform/config/ResponseResult.java new file mode 100644 index 0000000..f985b03 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/ResponseResult.java @@ -0,0 +1,57 @@ +package com.yfd.platform.config; + +import java.util.HashMap; + +public class ResponseResult extends HashMap { + private static final long serialVersionUID = 1L; + + public ResponseResult() { + } + + public static ResponseResult unlogin() { + return message("401", "未登录"); + } + + public static ResponseResult error() { + return error("操作失败!"); + } + + public static ResponseResult success() { + return success("操作成功!"); + } + + public static ResponseResult error(String msg) { + ResponseResult json = new ResponseResult(); + json.put("code", "1");//错误 + json.put("msg", msg); + return json; + } + + public static ResponseResult message(String code, String msg) { + ResponseResult json = new ResponseResult(); + json.put("code", code); + json.put("msg", msg); + return json; + } + + public static ResponseResult success(String msg) { + ResponseResult json = new ResponseResult(); + json.put("code", "0");//正常 + json.put("msg", msg); + return json; + } + + public static ResponseResult successData(Object obj) { + ResponseResult json = new ResponseResult(); + json.put("code", "0");//正常 + json.put("msg", "操作成功"); + json.put("data", obj); + return json; + } + + + public ResponseResult put(String key, Object value) { + super.put(key, value); + return this; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/SecurityConfig.java b/riis-system/src/main/java/com/yfd/platform/config/SecurityConfig.java new file mode 100644 index 0000000..48438c0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -0,0 +1,107 @@ +package com.yfd.platform.config; + +import com.yfd.platform.config.bean.LoginProperties; +import com.yfd.platform.exception.AccessDeniedHandExcetion; +import com.yfd.platform.exception.MyAuthenticationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +public class SecurityConfig extends WebSecurityConfigurerAdapter { + // 注入了加密算法 + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + @ConfigurationProperties(prefix = "login", ignoreUnknownFields = true) + public LoginProperties loginProperties() { + return new LoginProperties(); + } + + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Autowired + private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + + @Autowired + private MyAuthenticationException authenticationException; + + @Autowired + private AccessDeniedHandExcetion accessDeniedHandExcetion; + + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + httpSecurity + // 关闭 CSRF + .csrf().disable() + //不通过Session获取SecurityContext + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + //对于登录接口,允许匿名访问 + .authorizeRequests() + .antMatchers("/user/login").anonymous() + .antMatchers("/user/code").permitAll() + .antMatchers("/system/user/updatePassword").permitAll() + .antMatchers("/user/kickOutUser").permitAll() + .and() + .authorizeRequests() + // 放行静态资源 + .antMatchers( + HttpMethod.GET, + "/*.html", + "/**/*.html", + "/**/*.css", + "/**/*.js", + "/webSocket/**" + ).permitAll() + // 放行 swagger 文档 + .antMatchers("/swagger-ui.html").permitAll() + .antMatchers("/swagger-resources/**").permitAll() + .antMatchers("/webjars/**").permitAll() + .antMatchers("/*/api-docs").permitAll() + + // 放行 图片预览 + .antMatchers("/report/**").permitAll() + .antMatchers("/images/**").permitAll() + .antMatchers("/pageimage/**").permitAll() + .antMatchers("/avatar/**").permitAll() + .antMatchers("/menu/**").permitAll() + .antMatchers("/station/**").permitAll() + .antMatchers("/temp/**").permitAll() + .antMatchers("/systemurl/**").permitAll() + .antMatchers("/api/imageserver/upload").permitAll() + //测试放行所有访问 + .antMatchers("/**/**").permitAll() + //除上面外的所有请求全部需要签权认证 + .anyRequest().authenticated(); + + //允许跨域 + httpSecurity.cors(); + + //将jwt过来器加入到httpSecurity过滤器链中 + httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + + //配置异常处理器 + httpSecurity.exceptionHandling() + .authenticationEntryPoint(authenticationException) + .accessDeniedHandler(accessDeniedHandExcetion); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/SwaggerConfig.java b/riis-system/src/main/java/com/yfd/platform/config/SwaggerConfig.java new file mode 100644 index 0000000..3c32942 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -0,0 +1,135 @@ +package com.yfd.platform.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * swagger配置 + */ +@Configuration +public class SwaggerConfig { + + Boolean swaggerEnabled = true; + + @Bean + public Docket createRestGBApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("1. 系统管理") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.system.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + @Bean + public Docket createRestJobApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("2. 定时任务") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.quartz.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + + @Bean + public Docket createRestBaseDataApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("3. 基础数据管理") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.basedata.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + @Bean + public Docket createPatrolTasksApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("4. 巡视任务管理") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.patroltask.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + @Bean + public Docket createStationNodeApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("5. 机器人通讯") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.robotapi.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + @Bean + public Docket createRobotComandApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("6. 机器人控制") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.robotcomand.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + @Bean + public Docket createUavSystemApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("7. 无人机系统") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.uavapi.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + @Bean + public Docket createTestApi() { + return new Docket(DocumentationType.OAS_30) + .apiInfo(apiInfo()) + .groupName("8. 测试接口") + .select() + .apis(RequestHandlerSelectors.basePackage("com.yfd.platform.modules.test.controller")) + .paths(PathSelectors.any()) + .build() + .pathMapping("/") + .enable(swaggerEnabled); + } + + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("项目API 接口文档") + .description("") + .contact(new Contact("郑顺利", "郑顺利", "13910913995@163.com")) + .version("3.0") + .build(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/TencentSmsProperties.java b/riis-system/src/main/java/com/yfd/platform/config/TencentSmsProperties.java new file mode 100644 index 0000000..cd640ea --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/TencentSmsProperties.java @@ -0,0 +1,45 @@ +package com.yfd.platform.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 腾讯短信业务配置类(sms) + * + * 注意:@Component 和 @ConfigurationProperties(prefix = "tencent.sms") 需要同时使用 + * @version 1.0 + * @date 2021/12/30 18:32 + */ +@Data +@Component +@ConfigurationProperties(prefix = "tencent.sms") +public class TencentSmsProperties { + /** 腾讯云账户密钥对secretId */ + private String secretId; + + /** 腾讯云账户密钥对secretKey */ + private String secretKey; + + /** 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId(位于[应用管理]中的[应用列表]),示例: 1400006666 (1400开头)*/ + private String sdkAppId; + + /** 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看 */ + private String signName; + + /** 模板 ID 哈希表: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 + * key: 模板名称(自定义);value: 模板 ID(腾讯云已通过模板) + * */ + private Map templateIdMap; + + /** 国际/港澳台短信 SenderId: 国内短信填空,默认未开通(国内短信不需要填写此项),如需开通请联系 [sms helper] */ + String senderid = ""; + + + /**短信号码扩展号: 默认未开通,如需开通请联系 [sms helper] 个人不需要填写*/ + private String extendCode = ""; + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/config/ThreadToolConfig.java b/riis-system/src/main/java/com/yfd/platform/config/ThreadToolConfig.java new file mode 100644 index 0000000..5473220 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/ThreadToolConfig.java @@ -0,0 +1,38 @@ +package com.yfd.platform.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 全局线程池配置 + */ +@Configuration +@EnableAsync +public class ThreadToolConfig { + @Bean("myThreadPool") + public Executor msgThreadPool() { + //获取当前机器的核数 + int cpuNum = Runtime.getRuntime().availableProcessors(); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + //配置核心线程数cpuNum + executor.setCorePoolSize(cpuNum * 2); + //配置最大线程数cpuNum * 2 + executor.setMaxPoolSize(cpuNum * 2); + //配置队列大小 + executor.setQueueCapacity(300); + //空闲线程存活时间 + executor.setKeepAliveSeconds(60); + //配置线程池中的线程的名称前缀 + executor.setThreadNamePrefix("pool-thread-"); + // 拒绝策略,弃老策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); + //执行初始化 + executor.initialize(); + return executor; + } +} \ No newline at end of file diff --git a/riis-system/src/main/java/com/yfd/platform/config/WebConfig.java b/riis-system/src/main/java/com/yfd/platform/config/WebConfig.java new file mode 100644 index 0000000..225e7bc --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/WebConfig.java @@ -0,0 +1,99 @@ +package com.yfd.platform.config; + +import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheUtil; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import lombok.SneakyThrows; +import org.junit.jupiter.api.Order; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; +import java.io.File; +import java.util.List; +import java.util.Map; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Value("${file-space.system}") + private String systempath; + + @Value("${httpserver.patrolserver.snapfilepath}") + private String snapFilePath; + + @Value("${httpserver.patrolserver.tempfilepath}") + private String tempFilePath; + + @Value("${httpserver.patrolserver.alarmfilepath}") + private String alarmFilePath; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @Bean + public Map>> typeList() { + return substationDeviceService.getDeviceType(); + } + + @Bean + public Cache loginuserCache() { + return CacheUtil.newLRUCache(200);//用户登录缓存数 缺省200 + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = + new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOriginPattern("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + config.setMaxAge(3600L); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } + + @SneakyThrows + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 菜单图标访问路径 + + // 菜单图标 + String iconUrl ="file:" + systempath+ "menuicon"+ File.separator; + registry.addResourceHandler("/menu/**").addResourceLocations(iconUrl).setCachePeriod(0); + + registry.addResourceHandler("swagger-ui.html").addResourceLocations( + "classpath:/META-INF/resources/"); + + // 用户图片 + String systemUrl = "file:" + systempath + "user"+ File.separator; + registry.addResourceHandler("/avatar/**").addResourceLocations(systemUrl).setCachePeriod(0); + + // 变电站图片 + String stationUrl = "file:" + systempath + "station"+ File.separator; + registry.addResourceHandler("/station/**").addResourceLocations(stationUrl).setCachePeriod(0); + + // 巡视设备图片 + String patrolDeviceUrl ="file:" + systempath+ "patrolDevice"+ File.separator; + registry.addResourceHandler("/patrolDevice/**").addResourceLocations(patrolDeviceUrl).setCachePeriod(0); + // 预览巡视结果路径(图片或者视频) + String videoUrl = "file:" + snapFilePath; + registry.addResourceHandler("/video/**").addResourceLocations(videoUrl).setCachePeriod(0); + // 点位模板图片 + String deviceUrl = "file:" + tempFilePath; + registry.addResourceHandler("/temp/**").addResourceLocations(deviceUrl).setCachePeriod(0); + + // 告警图片 + String alarmUrl = "file:" + alarmFilePath; + registry.addResourceHandler("/alarm/**").addResourceLocations(alarmUrl).setCachePeriod(0); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/WebSocketConfig.java b/riis-system/src/main/java/com/yfd/platform/config/WebSocketConfig.java new file mode 100644 index 0000000..349ead0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/WebSocketConfig.java @@ -0,0 +1,16 @@ +package com.yfd.platform.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + + return new ServerEndpointExporter(); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/config/bean/LoginCode.java b/riis-system/src/main/java/com/yfd/platform/config/bean/LoginCode.java new file mode 100644 index 0000000..85c39a3 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/bean/LoginCode.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.bean; + +import lombok.Data; + +/** + * 登录验证码配置信息 + * + * @date: 2020/6/10 18:53 + */ +@Data +public class LoginCode { + + /** + * 验证码配置 + */ + private LoginCodeEnum codeType; + /** + * 验证码有效期 分钟 + */ + private Long expiration = 2L; + /** + * 验证码内容长度 + */ + private int length = 2; + /** + * 验证码宽度 + */ + private int width = 111; + /** + * 验证码高度 + */ + private int height = 36; + /** + * 验证码字体 + */ + private String fontName; + /** + * 字体大小 + */ + private int fontSize = 25; + + public LoginCodeEnum getCodeType() { + return codeType; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/bean/LoginCodeEnum.java b/riis-system/src/main/java/com/yfd/platform/config/bean/LoginCodeEnum.java new file mode 100644 index 0000000..e607ba6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/bean/LoginCodeEnum.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.bean; + +/** + * 验证码配置枚举 + * + * @date: 2020/6/10 17:40 + */ + +public enum LoginCodeEnum { + /** + * 算数 + */ + arithmetic, + /** + * 中文 + */ + chinese, + /** + * 中文闪图 + */ + chinese_gif, + /** + * 闪图 + */ + gif, + spec +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/bean/LoginProperties.java b/riis-system/src/main/java/com/yfd/platform/config/bean/LoginProperties.java new file mode 100644 index 0000000..a7abd5d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/bean/LoginProperties.java @@ -0,0 +1,109 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version loginCode.length.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-loginCode.length.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.bean; + +import cn.hutool.core.util.StrUtil; +import com.wf.captcha.*; +import com.wf.captcha.base.Captcha; +import com.yfd.platform.exception.BadConfigurationException; +import lombok.Data; +import java.awt.*; +import java.util.Objects; + +/** + * 配置文件读取 + * + * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 + */ +@Data +public class LoginProperties { + + /** + * 账号单用户 登录 + */ + private boolean singleLogin = false; + + private LoginCode loginCode; + /** + * 用户登录信息缓存 + */ + private boolean cacheEnable; + + public boolean isSingleLogin() { + return singleLogin; + } + + public boolean isCacheEnable() { + return cacheEnable; + } + + /** + * 获取验证码生产类 + * + * @return / + */ + public Captcha getCaptcha() { + if (Objects.isNull(loginCode)) { + loginCode = new LoginCode(); + if (Objects.isNull(loginCode.getCodeType())) { + loginCode.setCodeType(LoginCodeEnum.arithmetic); + } + } + return switchCaptcha(loginCode); + } + + /** + * 依据配置信息生产验证码 + * + * @param loginCode 验证码配置信息 + * @return / + */ + private Captcha switchCaptcha(LoginCode loginCode) { + Captcha captcha; + synchronized (this) { + switch (loginCode.getCodeType()) { + case arithmetic: + // 算术类型 https://gitee.com/whvse/EasyCaptcha + captcha = new ArithmeticCaptcha(loginCode.getWidth(), loginCode.getHeight()); + // 几位数运算,默认是两位 + captcha.setLen(loginCode.getLength()); + break; + case chinese: + captcha = new ChineseCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case chinese_gif: + captcha = new ChineseGifCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case gif: + captcha = new GifCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + case spec: + captcha = new SpecCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha.setLen(loginCode.getLength()); + break; + default: + throw new BadConfigurationException("验证码配置信息错误!正确配置查看 LoginCodeEnum "); + } + } + if(StrUtil.isNotBlank(loginCode.getFontName())){ + captcha.setFont(new Font(loginCode.getFontName(), Font.PLAIN, loginCode.getFontSize())); + } + return captcha; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/redis/RedisMsgListenConfig.java b/riis-system/src/main/java/com/yfd/platform/config/redis/RedisMsgListenConfig.java new file mode 100644 index 0000000..8b2f380 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/redis/RedisMsgListenConfig.java @@ -0,0 +1,40 @@ +package com.yfd.platform.config.redis; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; + + +/** + * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 + * + * @date: 2019年5月30日 上午10:58:25 + * + */ +@Configuration +@Order(value=1) +public class RedisMsgListenConfig { + + + + + /** + * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 + * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 + * + * @param connectionFactory + * @return + */ + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { + + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + //container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS)); + + return container; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/redis/RedisTemplateConfig.java b/riis-system/src/main/java/com/yfd/platform/config/redis/RedisTemplateConfig.java new file mode 100644 index 0000000..26f5f15 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/redis/RedisTemplateConfig.java @@ -0,0 +1,28 @@ +package com.yfd.platform.config.redis; + +import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisTemplateConfig { + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + // 使用fastJson序列化 + GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer(); + // value值的序列化采用fastJsonRedisSerializer + redisTemplate.setValueSerializer(fastJsonRedisSerializer); + redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); + + // key的序列化采用StringRedisSerializer + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/redis/RiisConstants.java b/riis-system/src/main/java/com/yfd/platform/config/redis/RiisConstants.java new file mode 100644 index 0000000..dbb5d2b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/redis/RiisConstants.java @@ -0,0 +1,18 @@ +package com.yfd.platform.config.redis; + +/** + * @description: 定义系统中的常量 + * + * @date: 2023-05-24 + * + */ +public class RiisConstants { + + //************************** redis KEY 前缀 ********************************* + + /** + * 摄像机是否处于工作状态 + */ + public static final String Patroldevice_IsWorking = "Patroldevice_IsWorking_"; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/thread/AsyncTaskExecutePool.java b/riis-system/src/main/java/com/yfd/platform/config/thread/AsyncTaskExecutePool.java new file mode 100644 index 0000000..cd8c9a1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/thread/AsyncTaskExecutePool.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 异步任务线程池装配类 + * @date 2019年10月31日15:06:18 + */ +@Slf4j +@Configuration +public class AsyncTaskExecutePool implements AsyncConfigurer { + + /** 注入配置类 */ + private final AsyncTaskProperties config; + + public AsyncTaskExecutePool(AsyncTaskProperties config) { + this.config = config; + } + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + //核心线程池大小 + executor.setCorePoolSize(config.getCorePoolSize()); + //最大线程数 + executor.setMaxPoolSize(config.getMaxPoolSize()); + //队列容量 + executor.setQueueCapacity(config.getQueueCapacity()); + //活跃时间 + executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); + //线程名字前缀 + executor.setThreadNamePrefix("el-async-"); + // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务 + // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + executor.initialize(); + return executor; + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return (throwable, method, objects) -> { + log.error("===="+throwable.getMessage()+"====", throwable); + log.error("exception method:"+method.getName()); + }; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/thread/AsyncTaskProperties.java b/riis-system/src/main/java/com/yfd/platform/config/thread/AsyncTaskProperties.java new file mode 100644 index 0000000..98bd980 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/thread/AsyncTaskProperties.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 线程池配置属性类 + * @date 2019年10月31日14:58:18 + */ +@Data +@Component +@ConfigurationProperties(prefix = "task.pool") +public class AsyncTaskProperties { + + private int corePoolSize; + + private int maxPoolSize; + + private int keepAliveSeconds; + + private int queueCapacity; +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/thread/TheadFactoryName.java b/riis-system/src/main/java/com/yfd/platform/config/thread/TheadFactoryName.java new file mode 100644 index 0000000..b935d02 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/thread/TheadFactoryName.java @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + +import org.springframework.stereotype.Component; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 自定义线程名称 + * + * @date 2019年10月31日17:49:55 + */ +@Component +public class TheadFactoryName implements ThreadFactory { + + private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public TheadFactoryName() { + this("el-pool"); + } + + private TheadFactoryName(String name){ + SecurityManager s = System.getSecurityManager(); + group = (s != null) ? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + //此时namePrefix就是 name + 第几个用这个工厂创建线程池的 + this.namePrefix = name + + POOL_NUMBER.getAndIncrement(); + } + + @Override + public Thread newThread(Runnable r) { + //此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程 + Thread t = new Thread(group, r, + namePrefix + "-thread-"+threadNumber.getAndIncrement(), + 0); + if (t.isDaemon()) { + t.setDaemon(false); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/config/thread/ThreadPoolExecutorUtil.java b/riis-system/src/main/java/com/yfd/platform/config/thread/ThreadPoolExecutorUtil.java new file mode 100644 index 0000000..90ffd50 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/config/thread/ThreadPoolExecutorUtil.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.config.thread; + + + +import com.yfd.platform.utils.SpringContextHolder; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * 用于获取自定义线程池 + * + * @date 2019年10月31日18:16:47 + */ +public class ThreadPoolExecutorUtil { + + public static ThreadPoolExecutor getPoll(){ + AsyncTaskProperties properties = SpringContextHolder.getBean(AsyncTaskProperties.class); + return new ThreadPoolExecutor( + properties.getCorePoolSize(), + properties.getMaxPoolSize(), + properties.getKeepAliveSeconds(), + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(properties.getQueueCapacity()), + new TheadFactoryName() + ); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/constant/Constant.java b/riis-system/src/main/java/com/yfd/platform/constant/Constant.java new file mode 100644 index 0000000..57afe6b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/constant/Constant.java @@ -0,0 +1,62 @@ +package com.yfd.platform.constant; + +import java.time.LocalDateTime; + +/** + * + * @Date: 2023/3/3 17:40 + * @Description: 常量类 + */ +public class Constant { + public static LocalDateTime now =LocalDateTime.now(); + public static final String LOGIN = "login:"; + public static final String TOKEN = "token:"; + public static final String USER_ID = "userid"; + public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + /** + * 用户登录 + */ + public static final String USER_LOGIN = "system-login:"; + public static final String CODE_KEY = "code-key-"; + + public static final String ROBOT_SEND_CODE = "robotSendCode-"; + public static final String CLOUD_SEND_CODE = "cloudHSendCode-"; + + public static final String SYSTEM_MESSAGE_SUCCESS = "server connected success"; + /** + * 验证码失效时间 + */ + public static final long CODE_EXPIRATION_TIME = 1000 * 60 * 2; + + /** + * 用于IP定位转换 + */ + public static final String REGION = "内网IP|内网IP"; + /** + * win 系统 + */ + public static final String WIN = "win"; + + /** + * mac 系统 + */ + public static final String MAC = "mac"; + + /** + * Redis Const + * 设备录像信息90天时长 + */ + public static final String REDIS_RECORD_TIMES_PRE = "DEVICERECORDTIMES_"; + + /** + * 常用接口 + */ + public static class Url { + + // IP归属地查询 + // public static final String IP_URL = "http://whois.pconline.com + // .cn/ipJson.jsp?ip=%s&json=true"; + public static final String IP_URL = "http://whois.pconline.com" + + ".cn/ipJson.jsp?ip=%s&json=true"; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/constant/SystemCode.java b/riis-system/src/main/java/com/yfd/platform/constant/SystemCode.java new file mode 100644 index 0000000..31fa134 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/constant/SystemCode.java @@ -0,0 +1,149 @@ +package com.yfd.platform.constant; + +/** + * 统一常量枚举类 + * 包含项目中所有字符串常量定义 + */ +public enum SystemCode { + + TYPE_SYSTEM_MESSAGE_CODE("251", "系统消息编码"), + + ONLINE_CODE("1", "在线"), + OFFLINE_CODE("0", "离线"), + COMMAND_REGISTER_CODE("1", "注册"), + COMMAND_HEARTBEAT_CODE("2", "心跳"), + COMMAND_RESPONSE_CODE("3", "响应(无 Items)"), + COMMAND_ITEM_RESPONSE_CODE("4", "响应(有 Items)"), + + TYPE_ROBOT_CONTROL_CODE("1", "机器人本体控制"), + COMMAND_REMOTE_RESET_CODE("1", "远方复位"), + COMMAND_SELF_CHECK_CODE("2", "系统自检"), + COMMAND_TURN_BACK_CODE("3", "一键返航"), + COMMAND_MANUAL_CHARGE_CODE("4", "手动充电"), + COMMAND_MODE_SWITCH_CODE("5", "控制模式切换"), + + TYPE_MODEL_UPDATE_CODE("11", "模型更新"), + + TYPE_DRONE_CONTROL_CODE("20001", "无人机"), + TYPE_FLIGHT_CONTROL_CODE("20002", "无人机飞行控制"), + TYPE_DRONE_PTZ_CODE("20003", "无人机云台"), + TYPE_DRONE_CAMERA_CODE("20004", "无人机相机"), + TYPE_NEST_CODE("20005", "无人机机巢"), + + TYPE_TASK_CONTROL_CODE("41", "任务控制"), + COMMAND_TASK_START_CODE("1", "任务启动"), + COMMAND_TASK_PAUSE_CODE("2", "任务暂停"), + COMMAND_TASK_RESUME_CODE("3", "任务继续"), + COMMAND_TASK_STOP_CODE("4", "任务停止"), + TASK_START_CODE("1", "任务启动"), + TASK_PAUSE_CODE("pause", "任务暂停"), + TASK_RESUME_CODE("resume", "任务继续"), + TASK_STOP_CODE("stop", "任务停止"), + TASK_DELETE_CODE("delete", "任务删除"), + TASK_RUN_CODE("runNow", "任务立即执行"), + + TYPE_EXAMINE_ISSUED_CODE("81", "检修区域下发"), + COMMAND_EXAMINE_CONFIG_CODE("4", "检修区域配置"), + + TYPE_TASK_ISSUED_CODE("101", "任务下发"), + TYPE_LINKAGE_TASK_ISSUED_CODE("102", "联动任务下发"), + COMMAND_TASK_CONFIG_CODE("1", "任务配置"), + + TYPE_MODEL_SYNC_CODE("61", "模型同步"), + COMMAND_MODEL_HOST_CODE("1", "巡视主机模型"), + COMMAND_MODEL_ROBOT_CODE("2", "机器人模型"), + COMMAND_MODEL_CAMERA_CODE("3", "摄像机模型"), + COMMAND_MODEL_DEVICE_CODE("4", "点位模型"), + COMMAND_MODEL_DRONE_CODE("5", "无人机模型"), + COMMAND_MODEL_VOICE_CODE("6", "声纹模型"), + COMMAND_MODEL_TASK_CODE("7", "任务文件"), + COMMAND_MODEL_EXAMINE_CODE("8", "检修区域配置文件"), + COMMAND_MODEL_MAP_CODE("9", "地图文件"), + + COMMAND_HOST_FILE_PATH("host_file_path", "巡视主机模型路径"), + COMMAND_ROBOT_FILE_PATH("robot_file_path", "机器人模型路径"), + COMMAND_VIDEO_FILE_PATH("video_file_path", "摄像机模型路径"), + COMMAND_DEVICE_FILE_PATH("device_file_path", "点位模型路径"), + COMMAND_DRONE_FILE_PATH("drone_file_path", "无人机模型路径"), + COMMAND_VOICE_FILE_PATH("voice_file_path", "声纹模型路径"), + COMMAND_TASK_FILE_PATH("task_file_path", "任务文件路径"), + COMMAND_OVERHAULAREA_FILE_PATH("overhaularea_file_path", "检修区域配置文件路径"), + COMMAND_MAP_FILE_PATH("map_file_path", "地图文件路径"), + + TYPE_DEVICE_STATUS_CODE("1", "巡视设备状态数据"), + TYPE_DEVICE_RUN_CODE("2", "巡视设备运行数据"), + TYPE_DEVICE_COORDINATE_CODE("3", "巡视设备坐标"), + TYPE_PATROL_ROUTE_CODE("4", "巡视路线"), + TYPE_ABNORMAL_ALARM_CODE("5", "巡视设备异常告警数据"), + TYPE_ENVIRONMENT_CODE("21", "环境数据"), + TYPE_TASK_STATUS_CODE("41", "任务状态数据"), + TYPE_TASK_RESULT_CODE("61", "巡视结果"), + TYPE_HOST_ALARM_CODE("62", "巡视主机告警数据"), + TYPE_SILENT_ALARM_CODE("63", "静默监视告警数据"), + TYPE_NORMAL_REPORT_CODE("68", " 正常样本信息上报"), + TYPE_NEST_STATUS_CODE("20001", "无人机机巢状态数据"), + TYPE_NEST_RUN_CODE("10004", "无人机机巢运行数据"), + + ITEM_BEGIN_TIME("begin_time", "统计开始时间"), + ITEM_END_TIME("end_time", "统计结束时间"), + ITEM_TOTAL_NUM("total_num", "满足查询条件总数"), + ITEM_VALID_NUM("valid_num", "查询有效数据"), + ITEM_PERCENT("percent", "统计百分比,有效值保留小数点后3位,如90.005%"), + ITEM_COMMISSION_TIME("commission_time", "投运时间"), + ITEM_REPORT_TIME("report_time", "上报时间"), + + + TYPE_DEVICE_REPORT_CODE("81", "巡视设备统计信息上报"), + ADD_ONLINE_DURATION("1", "累积在线时长总和"), + ADD_OFFLINE_DURATION("2", "累积离线次数总和"), + ADD_NORMAL_RUN_CODE("3", "累计连续正常运行天数"), + NORMAL_PATROL_CODE("4", "正常巡检天数"), + PATROL_ATTENDANCE_RATE_CODE("5", "巡检出勤率"), + VIDEO_INTEGRITY_RATE_CODE("6", "录像完整率"), + OTHER_RATE_CODE("7", "其他统计率"), + + + TYPE_TASK_COUNT_CODE("121", "巡视结果统计查询"), + COMMAND_CLOSE_LOOP_CODE("1", "巡视任务执行闭环率"), + COMMAND_ALARM_COMPLETE_CODE("2", "巡视告警人工审核完成率"), + COMMAND_ALARM_ACCURATE_CODE("3", "巡视告警准确率"), + COMMAND_PERSON_EXAMINE_CODE("4", "巡视结果人工审核完成率"), + COMMAND_MISS_CHECK_CODE("5", "巡检点位漏检率"), + + TYPE_SOLIDITY_INDEX_CODE("317", "可靠性指标统计查询"), + COMMAND_ALARM_EXACT_CODE("1", "巡视告警准确率"), + COMMAND_ALGORITHM_EXACT_CODE("2", "算法标签对应准确率"), + + TYPE_RELIABILITY_COUNT_CODE("318", "可靠性指标统计上报"), + ALARM_EXACT_CODE("1", "巡视告警准确率"), + ALGORITHM_EXACT_CODE("2", "算法标签对应准确率"), + + TYPE_ALGORITHM_REPORT_CODE("313", "算法资源信息上报"), + TYPE_ALGORITHM_PARAM_CODE("314", "获取当前该区域某站点的运行算法参数"), + TYPE_ALGORITHM_VERSION_CODE("315", "算法版本获取接口"), + TYPE_ALGORITHM_UPDATE_CODE("316", " 算法版本切换接口"), + + TYPE_ALARM_REPORT_CODE("312", "告警消息上报"), + + REPEAT_STATUS_CODE("100", "重发"), + SUCCESS_STATUS_CODE("200", "成功"), + REFUSE_STATUS_CODE("400", "拒绝"), + ERROR_STATUS_CODE("500", "错误"); + + private final String code; + private final String description; + + SystemCode(String code, String description) { + this.code = code; + this.description = description; + } + + public String getCode() { + return code; + } + + public String getDescription() { + return description; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/datasource/DataSource.java b/riis-system/src/main/java/com/yfd/platform/datasource/DataSource.java new file mode 100644 index 0000000..7c6d795 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/datasource/DataSource.java @@ -0,0 +1,17 @@ +package com.yfd.platform.datasource; + +import java.lang.annotation.*; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:48 + ******************************/ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataSource { + + String name() default ""; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/datasource/DataSourceAspect.java b/riis-system/src/main/java/com/yfd/platform/datasource/DataSourceAspect.java new file mode 100644 index 0000000..f20c0f8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/datasource/DataSourceAspect.java @@ -0,0 +1,55 @@ +package com.yfd.platform.datasource; + +import cn.hutool.core.util.StrUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:50 + ******************************/ +@Aspect +@Component +public class DataSourceAspect { + + @Pointcut("@annotation(com.yfd.platform.datasource.DataSource)") + public void dataSourcePointCut() { + + } + + private String DataBaseName; + + @Around("dataSourcePointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + if (StrUtil.isNotBlank(DataBaseName)){ + DynamicDataSource.setDataSource(DataBaseName); + }else { + DynamicDataSource.setDataSource("master"); + } + + try { + return point.proceed(); + } finally { + DynamicDataSource.clearDataSource(); + } + } + + public String getDataBase(Integer type){ + if (type == 1){ + DataBaseName="master"; + }else { + DataBaseName="slave"; + } + return DataBaseName; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java b/riis-system/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java new file mode 100644 index 0000000..e8b2202 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java @@ -0,0 +1,39 @@ +package com.yfd.platform.datasource; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.Map; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:47 + ******************************/ +public class DynamicDataSource extends AbstractRoutingDataSource { + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + @Override + protected Object determineCurrentLookupKey() { + return getDataSource(); + } + + public static void setDataSource(String dataSource) { + contextHolder.set(dataSource); + } + + public static String getDataSource() { + return contextHolder.get(); + } + + public static void clearDataSource() { + contextHolder.remove(); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/datasource/DynamicDataSourceConfig.java b/riis-system/src/main/java/com/yfd/platform/datasource/DynamicDataSourceConfig.java new file mode 100644 index 0000000..c2c78e6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/datasource/DynamicDataSourceConfig.java @@ -0,0 +1,40 @@ +package com.yfd.platform.datasource; + +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/****************************** + * 用途说明: + * 作者姓名: wxy + * 创建时间: 2022/9/23 17:45 + ******************************/ +@Configuration +@Component +public class DynamicDataSourceConfig { + + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource wglMasterDataSource(){ + return DruidDataSourceBuilder.create().build(); + } + + + + @Bean + @Primary + public DynamicDataSource dataSource(DataSource wglMasterDataSource, DataSource wglSlaveDataSource) { + Map targetDataSources = new HashMap<>(); + targetDataSources.put("master",wglMasterDataSource); + return new DynamicDataSource(wglMasterDataSource, targetDataSources); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/AccessDeniedHandExcetion.java b/riis-system/src/main/java/com/yfd/platform/exception/AccessDeniedHandExcetion.java new file mode 100644 index 0000000..4400bef --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/AccessDeniedHandExcetion.java @@ -0,0 +1,25 @@ +package com.yfd.platform.exception; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +@Component +public class AccessDeniedHandExcetion implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + JSONObject jobj=new JSONObject(); + jobj.putOnce("status","403"); + jobj.putOnce("msg","用户权限不足,不能访问"); + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().println(JSONUtil.toJsonStr(jobj)); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/BadConfigurationException.java b/riis-system/src/main/java/com/yfd/platform/exception/BadConfigurationException.java new file mode 100644 index 0000000..768dbe3 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/BadConfigurationException.java @@ -0,0 +1,97 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +/** + * 统一关于错误配置信息 异常 + * + * @date: 2020/6/10 18:06 + */ +public class BadConfigurationException extends RuntimeException { + /** + * Constructs a new runtime exception with {@code null} as its + * detail message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause}. + */ + public BadConfigurationException() { + super(); + } + + /** + * Constructs a new runtime exception with the specified detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public BadConfigurationException(String message) { + super(message); + } + + /** + * Constructs a new runtime exception with the specified detail message and + * cause.

Note that the detail message associated with + * {@code cause} is not automatically incorporated in + * this runtime exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public BadConfigurationException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new runtime exception with the specified cause and a + * detail message of {@code (cause==null ? null : cause.toString())} + * (which typically contains the class and detail message of + * {@code cause}). This constructor is useful for runtime exceptions + * that are little more than wrappers for other throwables. + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.4 + */ + public BadConfigurationException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new runtime exception with the specified detail + * message, cause, suppression enabled or disabled, and writable + * stack trace enabled or disabled. + * + * @param message the detail message. + * @param cause the cause. (A {@code null} value is permitted, + * and indicates that the cause is nonexistent or unknown.) + * @param enableSuppression whether or not suppression is enabled + * or disabled + * @param writableStackTrace whether or not the stack trace should + * be writable + * @since 1.7 + */ + protected BadConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/BadRequestException.java b/riis-system/src/main/java/com/yfd/platform/exception/BadRequestException.java new file mode 100644 index 0000000..d428c35 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/BadRequestException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +/** + * + * @date 2018-11-23 + * 统一异常处理 + */ +@Getter +public class BadRequestException extends RuntimeException{ + + private Integer status = BAD_REQUEST.value(); + + public BadRequestException(String msg){ + super(msg); + } + + public BadRequestException(HttpStatus status, String msg){ + super(msg); + this.status = status.value(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/ChildrenExistException.java b/riis-system/src/main/java/com/yfd/platform/exception/ChildrenExistException.java new file mode 100644 index 0000000..64bfd99 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/ChildrenExistException.java @@ -0,0 +1,19 @@ +package com.yfd.platform.exception; + +import org.springframework.util.StringUtils; + +/** + * @Date 2021/1/26 9:07 + * @Version 1.0 + */ +public class ChildrenExistException extends RuntimeException{ + + public ChildrenExistException(Class clazz, String field, String val) { + super(ChildrenExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " Children Exist"; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/EntityExistException.java b/riis-system/src/main/java/com/yfd/platform/exception/EntityExistException.java new file mode 100644 index 0000000..e04c0f6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/EntityExistException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +import org.springframework.util.StringUtils; + +/** + * + * @date 2018-11-23 + */ +public class EntityExistException extends RuntimeException { + + public EntityExistException(Class clazz, String field, String val) { + super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " existed"; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/EntityNotFoundException.java b/riis-system/src/main/java/com/yfd/platform/exception/EntityNotFoundException.java new file mode 100644 index 0000000..c4244ad --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/EntityNotFoundException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.exception; + +import org.springframework.util.StringUtils; + +/** + * + * @date 2018-11-23 + */ +public class EntityNotFoundException extends RuntimeException { + + public EntityNotFoundException(Class clazz, String field, String val) { + super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val)); + } + + private static String generateMessage(String entity, String field, String val) { + return StringUtils.capitalize(entity) + + " with " + field + " "+ val + " does not exist"; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/exception/MyAuthenticationException.java b/riis-system/src/main/java/com/yfd/platform/exception/MyAuthenticationException.java new file mode 100644 index 0000000..6abc25a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/exception/MyAuthenticationException.java @@ -0,0 +1,31 @@ +package com.yfd.platform.exception; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +@Component +public class MyAuthenticationException implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, org.springframework.security.core.AuthenticationException authException) throws IOException, ServletException { + // 在密码验证失败时进入此方法 + JSONObject jobj=new JSONObject(); + if(authException.getMessage().equals("用户账号不存在!")){ + jobj.putOnce("code","401"); + jobj.putOnce("msg","用户账号不存在/密码错误,登录失败!"); + }else{ + jobj.putOnce("code","401"); + jobj.putOnce("msg","用户登录信息失效,请重新登录!"); + } + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().println(JSONUtil.toJsonStr(jobj)); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/AlgorithmManufacturerVersionController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/AlgorithmManufacturerVersionController.java new file mode 100644 index 0000000..069bd90 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/AlgorithmManufacturerVersionController.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.controller; + +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 前端控制器 + *

+ * + * @author zhengsl + * @since 2024-05-06 + */ +@RestController +@RequestMapping("/basedata/algorithm-manufacturer-version") +@Api(value = "AlgorithmManufacturerVersionController", tags = "模型算法历史版本管理") +public class AlgorithmManufacturerVersionController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/AlgorithmModelController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/AlgorithmModelController.java new file mode 100644 index 0000000..1183f66 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/AlgorithmModelController.java @@ -0,0 +1,414 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.alibaba.fastjson.JSONArray; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.AlgorithmServerConfig; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.AlgorithmManufacturerVersion; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.modules.basedata.service.IAlgorithmManufacturerVersionService; +import com.yfd.platform.modules.basedata.service.IAlgorithmModelService; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.bytedeco.javacpp.presets.opencv_core; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +/** + *

+ * 算法模型表 前端控制器 + *

+ * + * + * @since 2023-08-25 + */ +@RestController +@RequestMapping("/basedata/algorithmmodel") +@Api(value = "AlgorithmModelController", tags = "模型算法") +@Slf4j +public class AlgorithmModelController { + + @Resource + private IAlgorithmModelService algorithmModelService; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private AlgorithmServerConfig algorithmServerConfig; + + @Resource + private IAlgorithmManufacturerVersionService manufacturerVersionService; + + @ApiOperation("获取算法模型列表") + @GetMapping("/getAlgorithmModelList") + public ResponseResult getAlgorithmModelList(Page> page, String stationId, String modelCode, + String modelName, String isnew, String runstate,String type) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(stationId)) { + queryWrapper.eq(AlgorithmModel::getStationId, stationId); + } + if (StrUtil.isNotEmpty(modelCode)) { + queryWrapper.eq(AlgorithmModel::getModelCode, modelCode); + } + if (StrUtil.isNotEmpty(isnew)) { + queryWrapper.eq(AlgorithmModel::getIsnew, isnew); + } + if (StrUtil.isNotEmpty(runstate)) { + queryWrapper.eq(AlgorithmModel::getRunstate, runstate); + } + if (StrUtil.isNotEmpty(type)) { + queryWrapper.eq(AlgorithmModel::getModelType, type); + } + if (StrUtil.isNotEmpty(modelName)) { + queryWrapper.like(AlgorithmModel::getModelName, modelName); + } + queryWrapper.eq(AlgorithmModel::getIsnew,"1").orderByAsc(AlgorithmModel::getStationName, AlgorithmModel::getModelType, + AlgorithmModel::getModelCode, AlgorithmModel::getVersion); + Page> mapPage = algorithmModelService.pageMaps(page, queryWrapper); + return ResponseResult.successData(mapPage); + } + + @ApiOperation("根据主键获取单个算法模型信息") + @GetMapping("/getAlgorithmModel") + public ResponseResult getAlgorithmModel(String id) { + AlgorithmModel algorithmModel = algorithmModelService.getById(id); + return ResponseResult.successData(algorithmModel); + } + + @ApiOperation("获取算法厂商历史版本列表") + @GetMapping("/getManufacturerPage") + public ResponseResult getManufacturerPage(Page> page,String manufacturer) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(manufacturer)) { + queryWrapper.eq(AlgorithmManufacturerVersion::getAlgorithmManufacturer, manufacturer); + } + Page> mapPage = manufacturerVersionService.pageMaps(page, queryWrapper); + return ResponseResult.successData(mapPage); + } + + + @Log(module = "算法模型", value = "新增或修改算法模型", type = "1") + @PostMapping("/addOrUpdateAlgorithmModel") + @ApiOperation("新增或修改算法模型") + public ResponseResult addOrUpdateAlgorithmModel(@RequestBody AlgorithmModel algorithmModel) { + if (StrUtil.isEmpty(algorithmModel.getId())) { + //新增算法 + algorithmModel.setId(IdUtil.fastSimpleUUID()); + algorithmModel.setRecordtime(LocalDateTime.now()); + algorithmModel.setRunstate("2"); + } + algorithmModel.setLastmodifier(SecurityUtils.getCurrentUsername()); + algorithmModel.setLastmodifydate(LocalDateTime.now()); + algorithmModelService.saveOrUpdate(algorithmModel); + return ResponseResult.success(); + } + + + @Log(module = "算法模型", value = "从算法平台下载模型", type = "1") + @PostMapping("/downloadAlgorithmModel") + @ApiOperation("从算法平台下载模型") + public ResponseResult downloadAlgorithmModel(String ModelId) { + //todo 从算法管理平台下载模型 + + return ResponseResult.success(); + } + + @Log(module = "算法模型", value = "将当前版本模型更新到智能分析主机", type = "1") + @PostMapping("/pushAlgorithmModelToAnalysisHost") + @ApiOperation("将当前版本模型更新到智能分析主机") + public ResponseResult pushAlgorithmModelToAnalysisHost(String ModelId) { + if (StrUtil.isBlank(ModelId)) { + return ResponseResult.error("参数为空"); + } + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("requestHostIp", httpServerConfig.getPatrolIp()); + jsonObject.putOnce("requestHostPort", httpServerConfig.getPatrolPort()); + jsonObject.putOnce("requestId", ModelId); + AlgorithmModel algorithmModel = algorithmModelService.getById(ModelId); + String modelFile = algorithmModel.getModelFile(); + String ftpUrl = "ftp://" + algorithmServerConfig.getServerIp() + ":2121/" + modelFile; + jsonObject.putOnce("algorithmPath", ftpUrl); + jsonObject.putOnce("requestHostAppname", httpServerConfig.getPatrolAppname()); + log.info(jsonObject.toString()); + httpRESTfulUtils.sendHttpPost("json", httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort13(), + "", "algorithmUpdate", jsonObject, null); + //todo 将当前版本模型更新到智能分析主机 + return ResponseResult.success(); + } + + // @Log(module = "算法版本获取接口", value = "巡视主机向算法管理平台认证获取token") + // @PostMapping("/getToken") + // @ApiOperation("巡视主机向算法管理平台认证获取token") + // public String getToken() { + // SysUser userInfo = userService.getUserInfo(); + // JSONObject jsonObject = new JSONObject(); + // jsonObject.putOnce("Username", userInfo.getUsername()); + // jsonObject.putOnce("Password", userInfo.getPassword()); + // com.alibaba.fastjson.JSONObject responseData = httpRESTfulUtils.sendHttpPost("json", + // httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort11(), "", + // "recogApi/token", jsonObject, null); + // return ResponseResult.successData(responseData); + // } + + /********************************** + * 用途说明: 获取算法管理平台调用Token + * 参数说明 + * 返回值说明: java.lang.String + ***********************************/ + private String getToken() { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("Username", algorithmServerConfig.getUsername()); + jsonObject.putOnce("Password", algorithmServerConfig.getPassword()); + // Map param = new HashMap<>(); + // param.put("param", jsonObject); + com.alibaba.fastjson.JSONObject responseData = httpRESTfulUtils.sendHttpPost("json", + algorithmServerConfig.getServerIp(), algorithmServerConfig.getHttpPort(), "", "recogApi/token", + jsonObject, null); + if (!SystemCode.SUCCESS_STATUS_CODE.getCode().equals(responseData.getString("code"))) { + return null; + } + return responseData.getString("Token"); + } + +// @Log(module = "算法版本获取接口", value = "获取当前该区域某站点的运行算法参数", type = "1") +// @PostMapping("/getRecogParams") +// @ApiOperation("获取当前该区域某站点的运行算法参数") +// public ResponseResult getRecogParams(String sectionId, String stationId) { +// JSONObject jsonObject = new JSONObject(); +// jsonObject.putOnce("sectionId", sectionId); +// jsonObject.putOnce("stationId", stationId); +// // Map param = new HashMap<>(); +// // param.put("param", jsonObject); +// String token = getToken(); +// com.alibaba.fastjson.JSONObject responseData = httpRESTfulUtils.sendHeaderHttpPost("json", +// algorithmServerConfig.getServerIp(), algorithmServerConfig.getHttpPort(), "", token, +// "runRecogApi/recogParams", jsonObject, null); +// String code = responseData.getString("code"); +// if (!"200".equals(code)) { +// String message = responseData.getString("message"); +// return ResponseResult.error(message); +// } +// JSONArray data = responseData.getJSONArray("data"); +// // 获取版本信息存放入数据库 +// for (int i = 0; i < data.size(); i++) { +// AlgorithmModel algorithmModel = data.getObject(i, AlgorithmModel.class); +// String algorithmmanufacturer = algorithmModel.getAlgorithmmanufacturer(); +// String version = algorithmModel.getVersion(); +// LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); +// queryWrapper.eq(AlgorithmModel::getAlgorithmmanufacturer, algorithmmanufacturer).eq(AlgorithmModel::getVersion, version); +// AlgorithmModel one = algorithmModelService.getOne(queryWrapper); +// if (one != null) { +// // 已经存在该版本数据 修改 +// algorithmModel.setId(one.getId()); +// algorithmModelService.updateById(algorithmModel); +// } else { +// LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); +// updateWrapper.eq(AlgorithmModel::getIsnew, "1").set(AlgorithmModel::getIsnew, "0"); +// algorithmModelService.update(updateWrapper); +// // 不存在该版本数据 新增 +// algorithmModel.setIsnew("1"); +// algorithmModel.setModelFile(version + ".zip"); +// algorithmModel.setModelCode("Model01"); +// algorithmModel.setModelName("Model01"); +// algorithmModel.setLastmodifier("算法更新"); +// algorithmModel.setRecordtime(LocalDateTime.now()); +// algorithmModel.setLastmodifydate(LocalDateTime.now()); +// algorithmModelService.save(algorithmModel); +// } +// } +// return ResponseResult.success(); +// } +// +// @Log(module = "算法版本获取接口", value = "获取算法厂商的算法版本信息", type = "1") +// @PostMapping("/getHistoryVersion") +// @ApiOperation("获取算法厂商的算法版本信息") +// public ResponseResult getHistoryVersion(String algorithmManufacturer, String token) { +// JSONObject jsonObject = new JSONObject(); +// jsonObject.putOnce("algorithmManufacturer", algorithmManufacturer); +// com.alibaba.fastjson.JSONObject responseData = httpRESTfulUtils.sendHeaderHttpPost("json", +// httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort11(), "", token, +// "recogApi/historyVersion", jsonObject, null); +// return ResponseResult.successData(responseData); +// } +// +// @Log(module = "算法版本获取接口", value = "实现某区域某站端算法版本的更新或回退", type = "1") +// @PostMapping("/updateAlgorithmVersion") +// @ApiOperation("实现某区域某站端算法版本的更新或回退") +// public ResponseResult updateAlgorithmVersion(String sectionId, String stationId, String version, String token) { +// JSONObject jsonObject = new JSONObject(); +// jsonObject.putOnce("sectionId", sectionId); +// jsonObject.putOnce("stationId", stationId); +// jsonObject.putOnce("version", version); +// com.alibaba.fastjson.JSONObject responseData = httpRESTfulUtils.sendHeaderHttpPost("json", +// httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort11(), "", token, +// "recogApi/updateAlgorithmVersion", jsonObject, null); +// String code = responseData.getString("code"); +// if (!"200".equals(code)) { +// String message = responseData.getString("message"); +// return ResponseResult.error(message); +// } +// String progress = "0%"; +// while (!"100%".equals(progress)) { +// JSONObject jsonObject1 = new JSONObject(); +// jsonObject1.putOnce("stationId", stationId); +// jsonObject1.putOnce("version", version); +// jsonObject1.putOnce("progress", progress); +// com.alibaba.fastjson.JSONObject responseData1 = httpRESTfulUtils.sendHeaderHttpPost("json", +// httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort11(), "", token, +// "recogApi/updateProgress", jsonObject1, null); +// String code1 = responseData1.getString("code"); +// if (!"200".equals(code1)) { +// String message = responseData1.getString("message"); +// return ResponseResult.error(message); +// } +// +// progress = responseData1.getString("progress"); +// } +// return ResponseResult.success(); +// } + + + @Log(module = "算法版本获取接口", value = "获取当前该区域某站点的运行算法参数", type = "1") + @GetMapping("/getRecogParams") + @ApiOperation("获取当前该区域某站点的运行算法参数") + @PreAuthorize("@el.check('select:model')") + public ResponseResult getRecogParams(String sectionId, String stationId, String type) { + algorithmModelService.getRecogParams(sectionId, stationId, type); + return ResponseResult.success(); + } + + @Log(module = "算法版本获取接口", value = "获取算法厂商的算法版本信息", type = "1") + @GetMapping("/getHistoryVersion") + @ApiOperation("获取算法厂商的算法版本信息") + @PreAuthorize("@el.check('select:model')") + public ResponseResult getHistoryVersion(String algorithmManufacturer, String sectionId, String stationId, + String type) { + algorithmModelService.getHistoryVersion(sectionId, stationId, type,algorithmManufacturer); + return ResponseResult.success(); + } + + @Log(module = "算法版本获取接口", value = "实现某区域某站端算法版本的更新或回退", type = "1") + @GetMapping("/updateAlgorithmVersion") + @ApiOperation("实现某区域某站端算法版本的更新或回退") + public ResponseResult updateAlgorithmVersion(String sectionId, String stationId, String type, String version) { + algorithmModelService.updateAlgorithmVersion(sectionId, stationId, type,version); + return ResponseResult.success(); + } + + /*@Log(module = "算法版本获取接口", value = "巡视主机向算法管理平台获取更新进度和结果") + @PostMapping("/updateProgress") + @ApiOperation("巡视主机向算法管理平台获取更新进度和结果") + public ResponseResult updateProgress(String stationId, String version, String progress, String token) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("stationId", stationId); + jsonObject.putOnce("version", version); + jsonObject.putOnce("progress", progress); + com.alibaba.fastjson.JSONObject responseData = httpRESTfulUtils.sendHeaderHttpPost("json", + httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort11(), "", token, + "recogApi/updateProgress", jsonObject, null); + return ResponseResult.successData(responseData); + }*/ + + @Log(module = "模型算法", value = "上传文件", type = "1") + @PostMapping("/uploadModelFile") + @ApiOperation("上传文件") + @ResponseBody + public ResponseResult uploadModelFile(MultipartFile file, String modelId) throws FileNotFoundException { + String filename = file.getOriginalFilename(); + String suffix = StrUtil.subAfter(filename, ".", true); + if (!"pdf".equals(suffix)) { + return ResponseResult.error("非法文件类型"); + } + String contentType = file.getContentType(); + if ("application/pdf".equals(contentType)) { + return ResponseResult.error("非法文件内容"); + } + algorithmModelService.uploadModelFile(file, modelId); + return ResponseResult.success(); + } + + @GetMapping("/viewModelFile") + @ApiOperation("预览模型文件") + public void viewModelFile(String fileName, HttpServletResponse response) throws IOException { + if (StrUtil.isBlank(fileName)) { + throw new RuntimeException("文件名是空的"); + } + if (fileName.contains("\\") || fileName.contains("/")) { + throw new RuntimeException("非法文件路径"); + } + String suffix = StrUtil.subAfter(fileName, ".", true).toLowerCase(); + if (!"pdf".equals(suffix)) { + throw new RuntimeException("非法文件类型"); + } + algorithmModelService.viewModelFile(fileName, response); + } + + @PostMapping("/deleteModelFile") + @ApiOperation("删除文件") + public ResponseResult deleteModelFile(String id, String fileName) { + if (StrUtil.isBlank(fileName)) { + throw new RuntimeException("文件名是空的"); + } + if (fileName.contains("\\") || fileName.contains("/")) { + throw new RuntimeException("非法文件路径"); + } + String suffix = StrUtil.subAfter(fileName, ".", true).toLowerCase(); + if (!"pdf".equals(suffix)) { + throw new RuntimeException("非法文件类型"); + } + int count = algorithmModelService.count(new LambdaQueryWrapper().eq(AlgorithmModel::getId, id).eq(AlgorithmModel::getModelFile, fileName)); + if (count < 0) { + return ResponseResult.error("删除失败"); + } + boolean ok = algorithmModelService.deleteModelFile(id, fileName); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @PostMapping("/uploadModel") + @ApiOperation("上传文件") + @ResponseBody + public ResponseResult uploadModel(String id,String type,String filePath) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("requestHostIp", httpServerConfig.getPatrolIp()); + jsonObject.putOnce("requestHostPort", httpServerConfig.getPatrolPort()); + jsonObject.putOnce("requestId", id); + jsonObject.putOnce("type", type); + String ftpUrl = + "ftp://" + algorithmServerConfig.getFtpUsername() + ":" + algorithmServerConfig.getFtpPassword() + "@" + algorithmServerConfig.getServerIp() + ":" + algorithmServerConfig.getFtpPort() + "/" + filePath; + jsonObject.putOnce("algorithmPath", ftpUrl); + log.info(jsonObject.toString()); + com.alibaba.fastjson.JSONObject jsonObject1 = httpRESTfulUtils.sendHttpPost("json", + httpServerConfig.getAnalyseIp(), httpServerConfig.getAnalysePort13(), "", + "algorithmUpdate", jsonObject, null); + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/DeviceChannelController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/DeviceChannelController.java new file mode 100644 index 0000000..31385de --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/DeviceChannelController.java @@ -0,0 +1,19 @@ +package com.yfd.platform.modules.basedata.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 设备下的通道信息(每个设备查看视频的主要信息表) + 前端控制器 + *

+ * + * + * @since 2023-04-21 + */ +@RestController +@RequestMapping("/basedata/device-channel") +public class DeviceChannelController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/LinkageSignalController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/LinkageSignalController.java new file mode 100644 index 0000000..d1c6e83 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/LinkageSignalController.java @@ -0,0 +1,396 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.ILinkageSignalService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.*; + +/** + *

+ * 联动信号关联表 前端控制器 + *

+ * + * @since 2023-04-06 + */ +@RestController +@RequestMapping("/basedata/linkage-signal") +@Api(value = "LinkageSignalController", tags = "联动信号配置") +public class LinkageSignalController { + + @Resource + private ILinkageSignalService linkageSignalService; + + @Resource + private ISubstationService substationService; + + @Resource + private ISubstationDeviceService substationDeviceService; + @Resource + private IStationRobotService stationRobotService; + @Resource + private ITaskService taskService; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @GetMapping("/getLinkageSignalList") + @ApiOperation("分页查询联动信号配置") + @PreAuthorize("@el.check('select:signal')") + public ResponseResult getLinkageSignalList(Page> page, String name, String dataStatus, + String linkageType) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(name)) { + queryWrapper.and(i->i.eq(LinkageSignal::getControlNum,name).or().like(LinkageSignal::getLinkageDeviceName, name)); + } + if (StrUtil.isNotBlank(dataStatus)) { + queryWrapper.eq(LinkageSignal::getDatastatus, dataStatus); + } + + if (StrUtil.isNotBlank(linkageType)) { + queryWrapper.eq(LinkageSignal::getLinkageType, linkageType); + } + Page> mapPage = linkageSignalService.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + records.forEach(r -> { + if (!Objects.isNull(r.get("stationId"))) { + String stationId = r.get("stationId").toString(); + Substation substation = substationService.getById(stationId); + if (substation != null) { + r.put("isStationFlag", substation.getIsStationFlag()); + r.put("online", substation.getOnline()); + } + } + }); + mapPage.setRecords(records); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getSubstationDeviceListById") + @ApiOperation("根据联动信号Id查询绑定的巡视信息") + public ResponseResult getSubstationDeviceListById(String id) { + LinkageSignal linkageSignal = linkageSignalService.getById(id); + String deviceIdList = linkageSignal.getDeviceIdList(); + //String videoPos = linkageSignal.getVideoPos(); + List> mapList = new ArrayList<>(); + if (StrUtil.isNotBlank(deviceIdList)) { + String[] split = deviceIdList.split(","); + for (String s : split) { + Map map = new HashMap<>(); + SubstationDevice substationDevice = substationDeviceService.getById(s); + if (substationDevice == null) { + continue; + } + map.put("deviceId", substationDevice.getDeviceId()); + map.put("deviceName", substationDevice.getDeviceName()); + map.put("stationName", substationDevice.getStationName()); + map.put("stationCode", substationDevice.getStationCode()); + map.put("areaName", substationDevice.getAreaName()); + map.put("bayName", substationDevice.getBayName()); + map.put("areaId", substationDevice.getAreaId()); + map.put("bayId", substationDevice.getBayId()); + mapList.add(map); + } + } /*else if (StrUtil.isNotBlank(videoPos)) { + List list = JSONUtil.toList(videoPos, Map.class); + for (Map map : list) { + mapList.add(map); + } + }*/ + return ResponseResult.successData(mapList); + } + + @PostMapping("/deleteBindSubstationDevice") + @ApiOperation("删除绑定的巡视点位(暂时不用)") + public ResponseResult deleteBindSubstationDevice(String id, String deviceId) { + LinkageSignal linkageSignal = linkageSignalService.getById(id); + String deviceIdList = linkageSignal.getDeviceIdList(); + // 拆分巡视点位id + String[] split = deviceIdList.split(","); + List ids = new ArrayList<>(split.length); + ids.addAll(Arrays.asList(split)); + ids.remove(null); + ids.remove(""); + // 删除指定的点位 + ids.remove(deviceId); + // 将删除的点位id用逗号拼接 + String joinIds = String.join(",", ids); + if (StrUtil.isBlank(joinIds)) { + linkageSignal.setLinkageType(""); + linkageSignal.setDatastatus("0"); + } + linkageSignal.setDeviceIdList(joinIds); + boolean ok = linkageSignalService.updateById(linkageSignal); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + /*@GetMapping("/getLinkageSignalById") + @ApiOperation("根据id查询联动信号配置") + public ResponseResult getLinkageSignalById(String id) { + return ResponseResult.successData(null); + } +*/ + @Log(module = "联动信号配置", value = "绑定巡视点位或者巡视设备", type = "1") + @PostMapping("/bindSubstationDevice") + @ApiOperation("绑定巡视点位或者巡视设备") + public ResponseResult bindSubstationDevice(@RequestBody LinkageSignal linkageSignal) { + linkageSignal.setLastmodifier(SecurityUtils.getCurrentUsername()); + String deviceIdList = linkageSignal.getDeviceIdList(); + /* String videoPos = linkageSignal.getVideoPos(); + if (StrUtil.isNotBlank(deviceIdList) || StrUtil.isNotBlank(videoPos)) { + linkageSignal.setDatastatus("1"); + } else { + linkageSignal.setLinkageType(""); + linkageSignal.setDatastatus("0"); + }*/ + if (StrUtil.isNotBlank(deviceIdList)) { + linkageSignal.setDatastatus("1"); + String[] split = deviceIdList.split(","); + String deviceId = split[0]; + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + linkageSignal.setStationId(substationDevice.getStationId()); + linkageSignal.setStationCode(substationDevice.getStationCode()); + linkageSignal.setStationName(substationDevice.getStationName()); + } else { + linkageSignal.setDatastatus("0"); + } + /* String linkageType = linkageSignal.getLinkageType(); + // 如果类型是2,则传的参数为 + if ("2".equals(linkageType)) { + JSONArray objects = JSONUtil.parseArray(videoPos); + List list = JSONUtil.toList(objects, Map.class); + List> listMap = new ArrayList<>(); + for (Map map : list) { + Map map1 = new HashMap<>(); + map1.put("patroldeviceCode", map.get("patroldeviceCode")); + map1.put("patroldeviceName", map.get("patroldeviceName")); + map1.put("typeName", map.get("typeName")); + map1.put("preset", map.get("preset")); + map1.put("stationCode", map.get("stationCode")); + listMap.add(map1); + } + String jsonStr = JSONUtil.toJsonStr(listMap); + linkageSignal.setVideoPos(jsonStr); + }*/ + linkageSignal.setLastmodifydate(LocalDateTime.now()); + boolean ok = linkageSignalService.updateById(linkageSignal); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("绑定失败"); + } + } + + @Log(module = "联动信号配置", value = "新增联动信号", type = "1") + @PostMapping("/addLinkageSignal") + @ApiOperation("新增联动信号") + @PreAuthorize("@el.check('add:signal')") + public ResponseResult addLinkageSignal(@RequestBody LinkageSignal linkageSignal) { + String controlNum = linkageSignal.getControlNum(); + int count = linkageSignalService.count(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, + controlNum)); + if (count > 0) { + return ResponseResult.error("监控索引号重复"); + } + String stationNum = linkageSignal.getStationCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Substation::getStationCode, stationNum); + List list = substationService.list(queryWrapper); + if (list == null) { + return ResponseResult.error("新增失败"); + } + Substation substation = list.get(0); + linkageSignal.setStationNum(substation.getStationCode()); + linkageSignal.setStationId(substation.getStationId()); + linkageSignal.setStationCode(substation.getStationCode()); + linkageSignal.setStationName(substation.getStationName()); + linkageSignal.setDatastatus("0"); + linkageSignal.setIsenable("0"); + linkageSignal.setTime(LocalDateTime.now()); + linkageSignal.setLastmodifier(SecurityUtils.getCurrentUsername()); + linkageSignal.setLastmodifydate(LocalDateTime.now()); + boolean ok = linkageSignalService.save(linkageSignal); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + + } + + @Log(module = "联动信号配置", value = "修改联动信号", type = "1") + @PostMapping("/updateLinkageSignalById") + @ApiOperation("修改联动信号") + @PreAuthorize("@el.check('update:signal')") + public ResponseResult updateLinkageSignalById(@RequestBody LinkageSignal linkageSignal) { + String id = linkageSignal.getId(); + String controlNum = linkageSignal.getControlNum(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(LinkageSignal::getId, id).eq(LinkageSignal::getControlNum, controlNum); + int count = linkageSignalService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("监控索引号重复"); + } + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + List list = substationService.list(queryWrapper1); + if (list == null) { + return ResponseResult.error("修改失败"); + } + Substation substation = list.get(0); + linkageSignal.setStationId(substation.getStationId()); + linkageSignal.setStationCode(substation.getStationCode()); + linkageSignal.setStationName(substation.getStationName()); + linkageSignal.setLastmodifier(SecurityUtils.getCurrentUsername()); + linkageSignal.setLastmodifydate(LocalDateTime.now()); + boolean ok = linkageSignalService.updateById(linkageSignal); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "联动信号配置", value = "修改联动信号状态", type = "1") + @PostMapping("/setLinkageSignalById") + @ApiOperation("修改联动信号状态") + public ResponseResult setLinkageSignalById(@RequestBody LinkageSignal linkageSignal) { + linkageSignal.setLastmodifier(SecurityUtils.getCurrentUsername()); + linkageSignal.setLastmodifydate(LocalDateTime.now()); + boolean ok = linkageSignalService.updateById(linkageSignal); + String id = linkageSignal.getId(); + LinkageSignal linkageSignal1 = linkageSignalService.getById(id); + if ("1".equals(linkageSignal.getIsenable()) && StrUtil.isNotBlank(linkageSignal1.getRobotCode())) { + createLinkageTask(linkageSignal1); + } + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + /********************************** + * 用途说明: 创建主辅联动任务 + * 参数说明 linkageSignal + * 返回值说明: void + ***********************************/ + private void createLinkageTask(LinkageSignal linkageSignal) { + Task task = new Task(); + String uuid = IdUtil.fastSimpleUUID(); + task.setTaskId(uuid); + task.setStationCode(linkageSignal.getStationCode()); + task.setStationName(linkageSignal.getStationName()); + String code = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"); + task.setTaskCode(code); + String taskName = "一件顺控任务-" + code; + if ("2".equals(linkageSignal.getLinkageType())) { + taskName = "智能联动任务-" + code; + } + task.setTaskName(taskName); + //特殊巡视 + task.setType("2"); + // 主辅设备联动 + task.setTaskType("4"); + //1立即执行 + task.setTaskTodoType("1"); + //优先级最高(4级) + task.setPriority("4"); + + task.setDeviceLevel(3); + task.setDeviceList(linkageSignal.getDeviceIdList()); + String currentUsername = "主辅联动"; + task.setLastmodifier(currentUsername); + task.setCreator(currentUsername); + Timestamp timestamp = new Timestamp(DateUtil.current()); + task.setLastmodifydate(timestamp); + task.setCreateTime(DateUtil.now()); + task.setIsenable("1"); + task.setRobotCode(linkageSignal.getRobotCode()); + task.setIsReport("0"); + task.setDatastatus("2"); + // 修改为索引号 + //代用了一键顺控的属性值 + task.setControlNum(linkageSignal.getControlNum()); + taskService.save(task); + // 联动信号 + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("task_code", task.getTaskCode()); + jsonObject.putOnce("task_name", taskName); + jsonObject.putOnce("priority", task.getPriority()); + jsonObject.putOnce("device_level", task.getDeviceLevel()); + jsonObject.putOnce("device_list", linkageSignal.getDeviceIdList()); + String robotCode = linkageSignal.getRobotCode(); + if (StrUtil.isNotBlank(robotCode)) { + List list = substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, robotCode)); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), + SystemCode.TYPE_LINKAGE_TASK_ISSUED_CODE.getCode(), SystemCode.COMMAND_TASK_CONFIG_CODE.getCode(), code, jsonObject.toString()); + } + } + + } + + @Log(module = "联动信号配置", value = "删除联动信号", type = "1") + @PostMapping("/deleteLinkageSignalById") + @ApiOperation("删除联动信号") + @PreAuthorize("@el.check('del:signal')") + public ResponseResult deleteLinkageSignalById(String ids) { + if (StrUtil.isBlank(ids)) { + return ResponseResult.error("参数为空"); + } + String[] split = ids.split(","); + for (String id : split) { + LinkageSignal linkageSignal = linkageSignalService.getById(id); + String datastatus = linkageSignal.getDatastatus(); + if ("1".equals(datastatus)) { + continue; + } + linkageSignalService.removeById(id); + } + return ResponseResult.success(); + } + + @PostMapping("/receiveFile") + @ApiOperation("接收文件") + public ResponseResult receiveFile(MultipartFile file, String type) throws Exception { + // 解析文件 + linkageSignalService.receiveFile(file, type); + return ResponseResult.success(); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/PatroldeviceResumeController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/PatroldeviceResumeController.java new file mode 100644 index 0000000..4ee61ae --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/PatroldeviceResumeController.java @@ -0,0 +1,18 @@ +package com.yfd.platform.modules.basedata.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 机器人及无人机履历表 前端控制器 + *

+ * + * + * @since 2023-04-26 + */ +@RestController +@RequestMapping("/basedata/patroldevice-resume") +public class PatroldeviceResumeController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationAreaController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationAreaController.java new file mode 100644 index 0000000..ec3b1e4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationAreaController.java @@ -0,0 +1,18 @@ +package com.yfd.platform.modules.basedata.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站_区域 前端控制器 + *

+ * + * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation-area") +public class SubstationAreaController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationBayController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationBayController.java new file mode 100644 index 0000000..14b7fa8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationBayController.java @@ -0,0 +1,18 @@ +package com.yfd.platform.modules.basedata.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站_间隔 前端控制器 + *

+ * + * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation-bay") +public class SubstationBayController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationComponentController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationComponentController.java new file mode 100644 index 0000000..f8119e8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationComponentController.java @@ -0,0 +1,18 @@ +package com.yfd.platform.modules.basedata.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 变电站_设备部件 前端控制器 + *

+ * + * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation-component") +public class SubstationComponentController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationController.java new file mode 100644 index 0000000..67954ee --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationController.java @@ -0,0 +1,512 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.component.nettyserver.NettyServerHandler; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.service.*; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysOrganizationService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.FileNotFoundException; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * 变电站 前端控制器 + *

+ * + * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation") +@Api(value = "SubstationController", tags = "变电站") +public class SubstationController { + + @Resource + private ISubstationService substationService; + + @Resource + private ISubstationAreaService substationAreaService; + + @Resource + private ISubstationBayService substationBayService; + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @Resource + private ISubstationMaindeviceService substationMaindeviceService; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @Resource + private ISysOrganizationService sysOrganizationService; + + @Resource + private IUserService userService; + + @Resource + NettyServerHandler nettyServerHandler; + + @ApiOperation("获取变电站树") + @GetMapping("/getSubstationBayTree") + @PreAuthorize("@el.check('select:substation')") + public ResponseResult getSubstationBayTree(String name) { + SysUser userInfo = userService.getUserInfo(); + Set orgCodeByUser = sysOrganizationService.getOrgCodeByUser(userInfo.getId()); + List> substationTree = substationService.getSubstationBayTree(name, orgCodeByUser); + return ResponseResult.successData(substationTree); + } + + @ApiOperation("获取变电站下拉列表") + @GetMapping("/getSubstationList") + public ResponseResult getSubstationList(String flag) { + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (usertype != 0) { + Set orgCodeByUser = sysOrganizationService.getOrgCodeByUser(userInfo.getId()); + if (orgCodeByUser == null || orgCodeByUser.size() == 0) { + return ResponseResult.successData(null); + } else { + queryWrapper.in(Substation::getStationCode, orgCodeByUser); + } + } + if (StrUtil.isNotBlank(flag)) { + queryWrapper.eq(Substation::getIsStationFlag, flag); + } + queryWrapper.eq(Substation::getDatastatus, "1").select(Substation::getStationId, Substation::getStationCode, + Substation::getStationName).orderByAsc(Substation::getCustom1); + List> listMaps = substationService.listMaps(queryWrapper); + return ResponseResult.successData(listMaps); + } + + @ApiOperation("获取所有变电站") + @GetMapping("/getAllSubstationList") + public ResponseResult getAllSubstationList(String flag) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(flag)) { + queryWrapper.eq(Substation::getIsStationFlag, flag); + } + queryWrapper.eq(Substation::getDatastatus, "1").select(Substation::getStationId, Substation::getStationCode, + Substation::getStationName,Substation::getCoordinate).orderByAsc(Substation::getCustom1); + List> listMaps = substationService.listMaps(queryWrapper); + return ResponseResult.successData(listMaps); + } + + + @ApiOperation("获取变电站下的区域") + @GetMapping("/getSubstationAreaByCode") + public ResponseResult getSubstationAreaByCode(String stationCode) { + if (StrUtil.isBlank(stationCode)) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationArea::getStationCode, stationCode).eq(SubstationArea::getDatastatus, "1"); + List list = substationAreaService.list(queryWrapper); + return ResponseResult.successData(list); + } + + @ApiOperation("获取变电站区域间隔导航树") + @GetMapping("/getSubstationTree") + public ResponseResult getSubstationTree(String stationId) { + List> substationTree = substationService.getSubstationTree(stationId); + return ResponseResult.successData(substationTree); + } + + @ApiOperation("获取变电站区域导航树") + @GetMapping("/getSubstationAreaTree") + public ResponseResult getSubstationAreaTree(String stationId) { + List> substationTree = substationService.getSubstationAreaTree(stationId); + return ResponseResult.successData(substationTree); + } + + @ApiOperation("获取区域下所有间隔") + @GetMapping("/getSubstationBayByArea") + public ResponseResult getSubstationBayByArea(String stationCode, String areaId, String bayName) { + List> substationBayList = substationService.getSubstationBayByArea(stationCode,areaId, bayName); + return ResponseResult.successData(substationBayList); + } + + @Log(module = "变电站区域间隔", value = "修改变电站",type = "1") + @ApiOperation("修改变电站") + @PostMapping("/updateSubstation") + public ResponseResult updateSubstation(@RequestBody Substation substation) { + if (substation == null) { + return ResponseResult.error("参数为空"); + } + boolean ok = substationService.updateSubstation(substation); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "修改变电站状态",type = "1") + @ApiOperation("修改变电站状态") + @PostMapping("/setSubstationStatus") + public ResponseResult setSubstationStatus(String stationId, String dataStatus) { + boolean ok = substationService.setSubstationStatus(stationId, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "修改区域状态",type = "1") + @ApiOperation("修改区域状态") + @PostMapping("/setSubstationAreaStatus") + public ResponseResult setSubstationAreaStatus(String areaId, String dataStatus) { + boolean ok = substationService.setSubstationAreaStatus(areaId, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "修改间隔状态",type = "1") + @ApiOperation("修改间隔状态") + @PostMapping("/setSubstationBayStatus") + public ResponseResult setSubstationBayStatus(String bayId, String dataStatus) { + boolean ok = substationService.setSubstationBayStatus(bayId, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "删除变电站",type = "1") + @ApiOperation("删除变电站") + @PostMapping("/deleteSubstation") + public ResponseResult deleteSubstation(@RequestParam String stationId) { + + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("参数为空"); + } + boolean ok = substationService.deleteSubstation(stationId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "变电站区域间隔", value = "删除区域",type = "1") + @ApiOperation("删除区域") + @PostMapping("/deleteSubstationArea") + public ResponseResult deleteSubstationArea(@RequestParam String areaId) { + + if (StrUtil.isBlank(areaId)) { + return ResponseResult.error("参数为空"); + } + boolean ok = substationService.deleteSubstationArea(areaId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "变电站区域间隔", value = "删除间隔",type = "1") + @ApiOperation("删除间隔") + @PostMapping("/deleteSubstationBay") + public ResponseResult deleteSubstationBay(@RequestParam String bayId) { + + if (StrUtil.isBlank(bayId)) { + return ResponseResult.error("参数为空"); + } + boolean ok = substationService.deleteSubstationBay(bayId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "变电站区域间隔", value = "修改变电站",type = "1") + @ApiOperation("修改变电站(暂时)") + @PostMapping("/updateSubstation_bak") + public ResponseResult updateSubstation_bak(@RequestBody Substation substation) { + if (substation == null) { + return ResponseResult.error("参数为空"); + } + String username = userService.getUsername(); + substation.setLastmodifier(username); + substation.setLastmodifydate(LocalDateTime.now()); + boolean ok = substationService.updateById(substation); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "修改变电站",type = "1") + @ApiOperation("根据id查询变电站(暂时)") + @PostMapping("/getSubstationById") + public ResponseResult getSubstationById(String stationId) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("参数为空"); + } + Substation substation = substationService.getById(stationId); + return ResponseResult.successData(substation); + } + + @Log(module = "变电站区域间隔", value = "修改区域",type = "1") + @ApiOperation("修改区域") + @PostMapping("/updateSubstationArea") + public ResponseResult updateSubstationArea(@RequestBody SubstationArea substationArea) { + if (substationArea == null) { + return ResponseResult.error("参数为空"); + } + String areaName = substationArea.getAreaName(); + String areaId = substationArea.getAreaId(); + String stationCode = substationArea.getStationCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationArea::getStationCode, stationCode).eq(SubstationArea::getAreaName, areaName).ne(SubstationArea::getAreaId, areaId); + int count = substationAreaService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("变电站下已经存在该区域"); + } + LambdaQueryWrapper bayQueryWrapper = new LambdaQueryWrapper<>(); + bayQueryWrapper.eq(SubstationBay::getAreaId, areaId); + int count1 = substationBayService.count(bayQueryWrapper); + if (count1 > 0) { + return ResponseResult.error("当前区域有间隔关联不能修改"); + } + int count2 = + substationPatroldeviceService.count(new LambdaQueryWrapper().eq(SubstationPatroldevice::getAreaId, areaId)); + if (count2 > 0) { + return ResponseResult.error("当前区域有巡视设备关联不能修改"); + } + substationArea.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationArea.setLastmodifydate(LocalDateTime.now()); + boolean ok = substationAreaService.updateById(substationArea); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "修改间隔",type = "1") + @ApiOperation("修改间隔") + @PostMapping("/updateSubstationBay") + public ResponseResult updateSubstationBay(@RequestBody SubstationBay substationBay) { + if (substationBay == null) { + return ResponseResult.error("参数为空"); + } + String bayName = substationBay.getBayName(); + String areaId = substationBay.getAreaId(); + String bayId = substationBay.getBayId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationBay::getAreaId, areaId).eq(SubstationBay::getBayName, bayName).ne(SubstationBay::getBayId, bayId); + int count = substationBayService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("区域下已经存在该间隔"); + } + LambdaQueryWrapper mainQueryWrapper = new LambdaQueryWrapper<>(); + mainQueryWrapper.eq(SubstationMaindevice::getBayId, bayId); + int count1 = substationMaindeviceService.count(mainQueryWrapper); + if (count1 > 0) { + return ResponseResult.error("当前间隔有主设备关联不能修改"); + } + int count2 = + substationDeviceService.count(new LambdaQueryWrapper().eq(SubstationDevice::getBayId, bayId)); + if (count2 > 0) { + return ResponseResult.error("当前间隔有点位关联不能修改"); + } + substationBay.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationBay.setLastmodifydate(LocalDateTime.now()); + boolean ok = substationBayService.updateById(substationBay); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "变电站区域间隔", value = "新增变电站",type = "1") + @ApiOperation("新增变电站") + @PostMapping("/addSubstation") + public ResponseResult addSubstation(@RequestBody Substation substation) { + if (substation == null) { + return ResponseResult.error("参数为空"); + } + String isStationFlag = substation.getIsStationFlag(); + if (StrUtil.isBlank(isStationFlag)) { + substation.setIsStationFlag("0"); + } + String stationCode = substation.getStationCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Substation::getStationCode, stationCode).or().eq(Substation::getNodeId, substation.getNodeId()); + int count = substationService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前变电站编号或节点已经存在"); + } + int count1 = substationService.count() + 1; + substation.setCustom1(count1); + substation.setLastmodifier(SecurityUtils.getCurrentUsername()); + substation.setLastmodifydate(LocalDateTime.now()); + substation.setDatastatus("1"); + boolean ok = substationService.save(substation); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "变电站区域间隔", value = "新增区域",type = "1") + @ApiOperation("新增区域") + @PostMapping("/addSubstationArea") + public ResponseResult addSubstationArea(@RequestBody SubstationArea substationArea) { + if (substationArea == null) { + return ResponseResult.error("参数为空"); + } + String stationCode = substationArea.getStationCode(); + String areaName = substationArea.getAreaName(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationArea::getStationCode, stationCode).eq(SubstationArea::getAreaName, areaName); + int count = substationAreaService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前区域已经存在"); + } + int count1 = + substationAreaService.count(new LambdaQueryWrapper().eq(SubstationArea::getStationCode, stationCode).orderByAsc(SubstationArea::getCustom1)) + 1; + substationArea.setCustom1(count1); + substationArea.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationArea.setLastmodifydate(LocalDateTime.now()); + substationArea.setDatastatus("1"); + boolean ok = substationAreaService.save(substationArea); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "变电站区域间隔", value = "新增间隔",type = "1") + @ApiOperation("新增间隔") + @PostMapping("/addSubstationBay") + public ResponseResult addSubstationBay(@RequestBody SubstationBay substationBay) { + if (substationBay == null) { + return ResponseResult.error("参数为空"); + } + String bayAreaName = substationBay.getAreaName(); + String bayName = substationBay.getBayName(); + String areaId = substationBay.getAreaId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationBay::getAreaName, bayAreaName).eq(SubstationBay::getBayName, bayName); + int count = substationBayService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前间隔已经存在"); + } + int count1 = + substationBayService.count(new LambdaQueryWrapper().eq(SubstationBay::getAreaId, + areaId).orderByAsc(SubstationBay::getCustom1)) + 1; + substationBay.setCustom1(count1); + substationBay.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationBay.setLastmodifydate(LocalDateTime.now()); + substationBay.setDatastatus("1"); + boolean ok = substationBayService.save(substationBay); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "变电站区域间隔", value = "上传图片",type = "1") + @PostMapping("/uploadImage") + @ApiOperation("上传图片") + @ResponseBody + public ResponseResult uploadImage(MultipartFile image) throws FileNotFoundException { + if (image.isEmpty()) { + return ResponseResult.error("文件是空的"); + } + String filename = image.getOriginalFilename(); + String suffix = StrUtil.subAfter(filename, ".", true).toLowerCase(); + if (!"png".equals(suffix) && !"jpg".equals(suffix) && !"gif".equals(suffix)) { + return ResponseResult.error("非法文件类型"); + } + String contentType = image.getContentType(); + if (!"image/jpeg".equals(contentType) && !"image/png".equals(contentType)&& !"image/gif".equals(contentType)) { + return ResponseResult.error("非法文件内容"); + } + String fileName = substationService.uploadIcon(image); + return ResponseResult.successData(fileName); + } + + @Log(module = "变电站区域间隔", value = "删除图片",type = "1") + @PostMapping("/deleteImage") + @ApiOperation("删除图片") + @ResponseBody + public ResponseResult deleteImage(String id, String filename) throws FileNotFoundException { + + boolean ok = substationService.deleteImage(id, filename); + if (ok) { + return ResponseResult.successData(true); + } else { + return ResponseResult.error(); + } + + } + + @PostMapping("/cancelImage") + @ApiOperation("取消修改") + @ResponseBody + public ResponseResult cancelImage(String id, String images) throws FileNotFoundException { + boolean ok = substationService.cancelImage(id, images); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + + @PostMapping("/uploadFile") + @ApiOperation("导入文件") + public ResponseResult uploadFile(MultipartFile file, String type) throws Exception { + String str = substationService.uploadExcel(file, type); + if (StrUtil.isBlank(str)) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @GetMapping("/getStationOnline") + @ApiOperation("获取边缘节点在线状态") + public int getStationOnline(String stationcode) throws Exception { + return substationService.getStationOnline(stationcode); + } + + @PostMapping("/testSendMsg") + @ApiOperation("测试向客户端发送数据") + public ResponseResult testSendMsg() throws Exception { + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationDeviceController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationDeviceController.java new file mode 100644 index 0000000..bf833cc --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationDeviceController.java @@ -0,0 +1,781 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationMaindevice; +import com.yfd.platform.modules.basedata.domain.TreeNode; +import com.yfd.platform.modules.basedata.service.IDeviceChannelService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationMaindeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysOrganizationService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.*; + +/** + *

+ * 变电站_巡视点位 前端控制器 + *

+ * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation-device") +@Api(value = "SubstationDeviceController", tags = "变电站_巡视点位") +@Slf4j +public class SubstationDeviceController { + + @Resource + private ISubstationMaindeviceService substationMaindeviceService; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @Resource + private IDeviceChannelService deviceChannelService; + + @Resource + private Map>> typeList; + + @Resource + private IUserService userService; + + @Resource + private ISysOrganizationService sysOrganizationService; + + @PostMapping("/getSubstationDeviceList") + @ApiOperation("分页查询巡视点位") + @PreAuthorize("@el.check('select:device')") + public ResponseResult getSubstationDeviceList(@RequestBody String param) { + JSONObject jsonObject = JSONUtil.parseObj(param); + Page> page = new Page<>(); + if (jsonObject.containsKey("current")) { + page.setCurrent(jsonObject.getInt("current")); + } + if (jsonObject.containsKey("size")) { + page.setSize(jsonObject.getInt("size")); + } + String stationId = jsonObject.getStr("stationId"); + String bayId = jsonObject.getStr("bayId"); + String areaId = jsonObject.getStr("areaId"); + String componentId = jsonObject.getStr("componentId"); + String mainDeviceId = jsonObject.getStr("mainDeviceId"); + String deviceName = jsonObject.getStr("deviceName"); + String ids = jsonObject.getStr("ids"); + String status = jsonObject.getStr("status"); + String dataType = jsonObject.getStr("dataType"); + String patrolDeviceCode = jsonObject.getStr("patrolDeviceCode"); + // Page> page, String stationId, String bayId, +// String componentId, String mainDeviceId, String deviceName, +// String ids, String status, String patrolDeviceCode + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationId)) { + queryWrapper.eq(SubstationDevice::getStationId, stationId); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.eq(SubstationDevice::getBayId, bayId); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationDevice::getAreaId, areaId); + } + if (StrUtil.isNotBlank(dataType)) { + queryWrapper.like(SubstationDevice::getDataType, dataType); + } + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationDevice::getDeviceName, deviceName); + } + if (StrUtil.isNotBlank(ids)) { + String[] split = ids.split(","); + queryWrapper.notIn(SubstationDevice::getDeviceId, Arrays.asList(split)); + } + // 根据部件查询 + if (StrUtil.isNotBlank(componentId)) { + queryWrapper.eq(SubstationDevice::getComponentId, componentId); + } + + // 根据主设备查询 + if (StrUtil.isNotBlank(mainDeviceId)) { + queryWrapper.eq(SubstationDevice::getMainDeviceId, mainDeviceId); + } + if ("0".equals(status)) { + int count = substationDeviceService.count(queryWrapper); + page.setSize(count); + } +// if (StrUtil.isNotBlank(patrolDeviceCode)) { +// List idList = substationDeviceService.getDeviceByCode(patrolDeviceCode); +// if (idList.size() > 0) { +// queryWrapper.in(SubstationDevice::getDeviceId, idList); +// } +// } + queryWrapper.orderByAsc(SubstationDevice::getDeviceCode); + Page> mapPage = substationDeviceService.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + List> deviceRecords = new ArrayList<>(); + for (Map record : records) { + record.remove("lastmodifydate"); + deviceRecords.add(record); + } + mapPage.setRecords(deviceRecords); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getAlarmThresholdList") + @ApiOperation("分页查询巡视点位告警阈值") + public ResponseResult getAlarmThresholdList(Page> page, String stationId, String areaId, + String bayId, String componentId, + String mainDeviceId, String deviceName, String metertype, + String recognitionType) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + if (usertype != 0) { + Set orgCodeByUser = sysOrganizationService.getOrgCodeByUser(userInfo.getId()); + if (orgCodeByUser != null && orgCodeByUser.size() > 0) { + queryWrapper.in(SubstationDevice::getStationCode, orgCodeByUser); + } + } + // 根据条件查询 + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationDevice::getDeviceName, deviceName); + } + if (StrUtil.isNotBlank(stationId)) { + queryWrapper.eq(SubstationDevice::getStationId, stationId); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationDevice::getAreaId, areaId); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.eq(SubstationDevice::getBayId, bayId); + } + // 根据部件查询 + if (StrUtil.isNotBlank(componentId)) { + queryWrapper.eq(SubstationDevice::getComponentId, componentId); + } + // 根据主设备查询 + if (StrUtil.isNotBlank(mainDeviceId)) { + queryWrapper.eq(SubstationDevice::getMainDeviceId, mainDeviceId); + } + if (StrUtil.isNotBlank(metertype)) { + queryWrapper.eq(SubstationDevice::getMeterType, metertype); + } + if (StrUtil.isNotBlank(recognitionType)) { + queryWrapper.like(SubstationDevice::getRecognitionTypeNameList, recognitionType); + } + queryWrapper.orderByAsc(SubstationDevice::getDeviceCode); + Page> mapPage = substationDeviceService.pageMaps(page, queryWrapper); + return ResponseResult.successData(mapPage); + } + + @Log(module = "报警阈值设置", value = "批量修改告警阈值", type = "1") + @PostMapping("/updateAlarmThreshold") + @ApiOperation("批量修改告警阈值") + public ResponseResult updateAlarmThreshold(@RequestBody SubstationDevice substationDevice) { + if (substationDevice == null) { + return ResponseResult.error("未选择修改的列"); + } + String deviceId = substationDevice.getDeviceId(); + if (StrUtil.isBlank(deviceId)) { + return ResponseResult.error("未选择修改的列"); + } + String[] split = deviceId.split(","); + for (String id : split) { + SubstationDevice substationDevice1 = substationDeviceService.getById(id); + Double earlyMin = substationDevice.getEarlyMin(); + Double earlyMax = substationDevice.getEarlyMax(); + Double sameMin = substationDevice.getSameMin(); + Double sameMax = substationDevice.getSameMax(); + Double seriousMin = substationDevice.getSeriousMin(); + Double seriousMax = substationDevice.getSeriousMax(); + Double criticalMin = substationDevice.getCriticalMin(); + Double criticalMax = substationDevice.getCriticalMax(); + substationDevice1.setEarlyMin(NumberUtil.round(earlyMin, 2).doubleValue()); + substationDevice1.setEarlyMax(NumberUtil.round(earlyMax, 2).doubleValue()); + substationDevice1.setSameMin(NumberUtil.round(sameMin, 2).doubleValue()); + substationDevice1.setSameMax(NumberUtil.round(sameMax, 2).doubleValue()); + substationDevice1.setSeriousMin(NumberUtil.round(seriousMin, 2).doubleValue()); + substationDevice1.setSeriousMax(NumberUtil.round(seriousMax, 2).doubleValue()); + substationDevice1.setCriticalMin(NumberUtil.round(criticalMin, 2).doubleValue()); + substationDevice1.setCriticalMax(NumberUtil.round(criticalMax, 2).doubleValue()); + substationDeviceService.updateById(substationDevice1); + } + return ResponseResult.success(); + } + + @GetMapping("/getDeviceType") + @ApiOperation("获取类型") + public ResponseResult getDeviceType() { + Map>> types = typeList; + return ResponseResult.successData(types); + } + + @Log(module = "巡视点位设置", value = "新增巡视点位", type = "1") + @PostMapping("/addSubstationDevice") + @ApiOperation("新增巡视点位") + @PreAuthorize("@el.check('add:device')") + public ResponseResult addSubstationDevice(@RequestBody SubstationDevice substationDevice) { + // 主设备id + String mainDeviceId = substationDevice.getMainDeviceId(); + // 部件id + String componentId = substationDevice.getComponentId(); + // 设备点位编号 + String deviceCode = substationDevice.getDeviceCode(); + // 同一个变电站下不可存在相同编号的巡视点位 + LambdaQueryWrapper deviceLambdaQueryWrapper = new LambdaQueryWrapper<>(); + deviceLambdaQueryWrapper.eq(SubstationDevice::getDeviceCode, deviceCode).eq(SubstationDevice::getStationId, + substationDevice.getStationId()); + int count = substationDeviceService.count(deviceLambdaQueryWrapper); + if (count > 0) { + return ResponseResult.error("巡视点位已经存在"); + } + String recognitionTypeList1 = substationDevice.getRecognitionTypeList(); + // 根据识别类型判断文件类型 + if (!Objects.isNull(recognitionTypeList1)) { + if ("4".equals(recognitionTypeList1)) { + substationDevice.setSaveTypeList("1"); + } else if ("5".equals(recognitionTypeList1)) { + substationDevice.setSaveTypeList("3"); + } else if ("6".equals(recognitionTypeList1)) { + substationDevice.setSaveTypeList("4"); + } else { + substationDevice.setSaveTypeList("2"); + } + } + String recognitionTypeList = substationDevice.getRecognitionTypeList(); + String[] split = recognitionTypeList.split(","); + List recognitions = new ArrayList<>(Arrays.asList(split)); + List> maps2 = typeList.get("recognition_type"); + List recognitionNames = new ArrayList<>(); + for (Map map : maps2) { + String itemcode = map.get("itemcode").toString(); + if (recognitions.contains(itemcode)) { + String dictname = map.get("dictname").toString(); + recognitionNames.add(dictname); + } + } + String join1 = String.join(",", recognitionNames); + substationDevice.setRecognitionTypeNameList(join1); + SubstationMaindevice substationMaindevice = substationMaindeviceService.getById(mainDeviceId); + if (substationMaindevice != null) { + substationDevice.setMainDeviceName(substationMaindevice.getMainDeviceName()); + } + SubstationComponent substationComponent = substationMaindeviceService.getComponentById(componentId); + if (substationComponent != null) { + substationDevice.setComponentName(substationComponent.getComponentName()); + } + substationDevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationDevice.setLastmodifydate(LocalDateTime.now()); + substationDevice.setDatastatus("1"); + boolean ok = substationDeviceService.save(substationDevice); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @GetMapping("/getCameraById") + @ApiOperation("查询关联摄像头") + public ResponseResult getCameraById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + SubstationDevice substationDevice = substationDeviceService.getById(id); + String videoPos = substationDevice.getVideoPos(); + //Json转List + JSONObject objects = JSONUtil.parseObj(videoPos); + Map maps = JSONUtil.toBean(objects, Map.class); + return ResponseResult.successData(maps); + } + + @Log(module = "巡视点位设置", value = "修改巡视点位", type = "1") + @PostMapping("/updateSubstationDeviceById") + @ApiOperation("修改巡视点位") + @PreAuthorize("@el.check('update:device')") + public ResponseResult updateSubstationDeviceById(@RequestBody SubstationDevice substationDevice) { + if (substationDevice == null) { + return ResponseResult.error("参数为空"); + } + String deviceId = substationDevice.getDeviceId(); + String deviceCode = substationDevice.getDeviceCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(SubstationDevice::getDeviceId, deviceId).eq(SubstationDevice::getDeviceCode, deviceCode); + int count = substationDeviceService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前巡视点位已经存在"); + } + String recognitionTypeList = substationDevice.getRecognitionTypeList(); + // 根据识别类型判断文件类型 + if (!Objects.isNull(recognitionTypeList)) { + if ("4".equals(recognitionTypeList)) { + substationDevice.setSaveTypeList("1"); + } else if ("5".equals(recognitionTypeList)) { + substationDevice.setSaveTypeList("3"); + } else if ("6".equals(recognitionTypeList)) { + substationDevice.setSaveTypeList("4"); + } else { + substationDevice.setSaveTypeList("2"); + } + } + String[] split = recognitionTypeList.split(","); + List recognitions = new ArrayList<>(Arrays.asList(split)); + List> maps2 = typeList.get("recognition_type"); + List recognitionNames = new ArrayList<>(); + for (Map map : maps2) { + String itemcode = map.get("itemcode").toString(); + if (recognitions.contains(itemcode)) { + String dictname = map.get("dictname").toString(); + recognitionNames.add(dictname); + } + } + String join1 = String.join(",", recognitionNames); + substationDevice.setRecognitionTypeNameList(join1); + substationDevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationDevice.setLastmodifydate(LocalDateTime.now()); + SubstationComponent component = substationMaindeviceService.getComponentById(substationDevice.getComponentId()); + if (component != null) { + substationDevice.setComponentName(component.getComponentName()); + } + boolean ok = substationDeviceService.updateById(substationDevice); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @PostMapping("/updateSubstationDevice") + @ApiOperation("临时修改巡视点位") + public ResponseResult updateSubstationDevice(@RequestBody SubstationDevice substationDevice) { + substationDeviceService.updateById(substationDevice); + return ResponseResult.success(); + } + + @GetMapping("/getSubstationDeviceById") + @ApiOperation("根据Id查询巡视点位") + public ResponseResult getSubstationDeviceById(String id) { + SubstationDevice substationDevice = substationDeviceService.getById(id); + return ResponseResult.successData(substationDevice); + } + + @Log(module = "巡视点位设置", value = "删除巡视点位", type = "1") + @PostMapping("/deleteSubstationDeviceById") + @ApiOperation("删除巡视点位") + @PreAuthorize("@el.check('del:device')") + public ResponseResult deleteSubstationDeviceById(String deviceId) { + if (StrUtil.isBlank(deviceId)) { + return ResponseResult.error("参数为空"); + } + boolean ok = substationDeviceService.deleteSubstationDeviceById(deviceId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "巡视点位设置", value = "设置巡视点位状态是否启用", type = "1") + @PostMapping("/setSubstationDeviceStatus") + @ApiOperation("设置巡视点位状态是否启用") + public ResponseResult setSubstationDeviceStatus(String deviceId, String dataStatus) { + boolean ok = substationDeviceService.setSubstationDeviceStatus(deviceId, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + /*********************************** + * 用途说明:保存图片模板 + * 参数说明 + * deviceId 点位ID + * multipartFile 文件对象 + * 返回值说明: 文件名 + ************************************/ + @ApiOperation("截图并且保存图片模板") + @PostMapping(value = "/saveImgTemplate") + public ResponseResult saveImgTemplate(HttpServletResponse response, String deviceId, String devicecode, + String channelcode) throws Exception { + if (StrUtil.isBlank(deviceId)) { + ResponseResult.error("参数为空"); + } + if (StrUtil.isBlank(devicecode) && StrUtil.isBlank(channelcode)) { + return ResponseResult.error("没有通道信息"); + } + String filename = substationDeviceService.saveImgTemplate(response, deviceId, devicecode, channelcode); + return ResponseResult.successData(filename); + } + + /*********************************** + * 用途说明:新增40000条点位数据 + * 参数说明 + * deviceId 点位ID + * multipartFile 文件对象 + * 返回值说明: 文件名 + ************************************/ + @ApiOperation("新增40000条点位数据") + @PostMapping(value = "/deviceBatchAdd") + public ResponseResult deviceBatchAdd() { + + List deviceList = new ArrayList<>(); + for (int i = 1; i < 40000; i++) { + String code = "20230802" + i; + String name = "测试20230802" + i; + SubstationDevice substationDevice = new SubstationDevice(); + substationDevice.setDeviceCode(code); + substationDevice.setDeviceName(name); + substationDevice.setStationId("9b4e9d9f70b2256a69dfcbdecb13c960"); + substationDevice.setStationName("东南区220KV标准型变电站"); + substationDevice.setStationCode("0001"); + substationDevice.setAreaId("7015de8df7881a1ce473b9180a21ba0d"); + substationDevice.setAreaName("主变区域"); + substationDevice.setBayId("1c116533607071930ab9dc85a508064f"); + substationDevice.setBayName("主变间隔2#"); + substationDevice.setMainDeviceId("b19198173d2325c29a114c98cd06efd4"); + substationDevice.setMainDeviceName("2#油浸式变压器"); + substationDevice.setDeviceType("1"); + substationDevice.setComponentId("6f7ef2895a58be8d8b951c74d94bcbeb"); + substationDevice.setComponentName("06.01测试部件"); + substationDevice.setMeterType("4"); + substationDevice.setAppearanceType("6"); + substationDevice.setRecognitionTypeList("{\"device_code\":\"\",\"device_pos\":\"\",\"robot_code\":\"\"," + + "\"robot_pos\":\"\",\"uav_code\":\"\"}"); + substationDevice.setDatastatus("1"); + substationDevice.setLastmodifier("测试40000条数据"); + substationDevice.setLastmodifydate(LocalDateTime.now()); + deviceList.add(substationDevice); + } + long l = System.currentTimeMillis(); + log.info("-----------------------------开始保存--------------------------------------"); + substationDeviceService.saveBatch(deviceList); + log.info("-----------------------------保存结束--------------------------------------"); + long l1 = System.currentTimeMillis(); + log.info("耗时" + (l1 - l) / 1000); + return ResponseResult.success(); + } + + @PostMapping("/uploadFile") + @ApiOperation("导入文件") + public ResponseResult uploadFile(MultipartFile file) throws Exception { + String str = substationDeviceService.uploadExcel(file); + if (StrUtil.isBlank(str)) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @PostMapping("/deviceDownloadFile") + @ApiOperation("导出点位") + public void deviceDownloadFile(Page> page, String bayId, + String componentId, String mainDeviceId, String deviceName, + HttpServletResponse response) throws IOException { + substationDeviceService.deviceDownloadFile(page, bayId, componentId, mainDeviceId, deviceName, response); + } + + @GetMapping("/getComponentTree") + @ApiOperation("获取部件树") + public ResponseResult getComponentTree(String bayId, String deviceName) { + List mapList = substationDeviceService.getComponentTree(bayId, deviceName); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getBayTree") + @ApiOperation("获取间隔树") + public ResponseResult getBayTree(String stationCode, String deviceName) { + TreeNode treeNode = substationDeviceService.getBayTree(stationCode, deviceName); + return ResponseResult.successData(treeNode); + } + + @GetMapping("/getDeviceList") + @ApiOperation("获取点位信息") + public ResponseResult getDeviceList(String componentId, String deviceName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationDevice::getDeviceName, deviceName); + } + queryWrapper.eq(SubstationDevice::getComponentId, componentId).select(SubstationDevice::getDeviceId, + SubstationDevice::getDeviceName, SubstationDevice::getPatroldeviceJson, + SubstationDevice::getComponentId); + List> mapList = substationDeviceService.listMaps(queryWrapper); + return ResponseResult.successData(mapList); + } + + @PostMapping("/deviceModelDownload") + @ApiOperation("导出点位") + public void deviceModelDownload(HttpServletResponse response) throws IOException { + List substationDevices = substationDeviceService.list(); + List> mapList = new ArrayList<>(); + for (SubstationDevice substationDevice : substationDevices) { + Map map = new HashMap<>(); + map.put("station_name", substationDevice.getStationName()); + map.put("station_code", substationDevice.getStationCode()); + map.put("area_id", substationDevice.getAreaId()); + map.put("area_name", substationDevice.getAreaName()); + map.put("device_id", substationDevice.getDeviceId()); + map.put("device_name", substationDevice.getDeviceName()); + map.put("component_id", substationDevice.getComponentId()); + map.put("component_name", substationDevice.getComponentName()); + map.put("bay_id", substationDevice.getBayId()); + map.put("bay_name", substationDevice.getBayName()); + map.put("main_device_id", substationDevice.getMainDeviceId()); + map.put("main_device_name", substationDevice.getMainDeviceName()); + map.put("device_type", substationDevice.getDeviceType()); + map.put("meter_type", substationDevice.getMeterType()); + map.put("appearance_type", substationDevice.getAppearanceType()); + map.put("save_type_list", substationDevice.getSaveTypeList()); + map.put("recognition_type_list", substationDevice.getRecognitionTypeList()); + map.put("phase", substationDevice.getPhase()); + map.put("device_info", substationDevice.getDeviceInfo()); + map.put("data_type", substationDevice.getDataType()); + map.put("lower_value", substationDevice.getLowerValue()); + map.put("upper_value", substationDevice.getUpperValue()); + map.put("video_pos", ""); + mapList.add(map); + } + StringBuilder xml = new StringBuilder(); + xml.append("\n"); + xml.append("<").append("Device_Model").append(">\n"); + for (Map map : mapList) { + xml.append("\n"); + } + xml.append(""); + // 设置响应头 + response.setContentType("application/xml"); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); + String fileName = "device_model.xml"; + response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + + // 写入响应体 + try (PrintWriter out = response.getWriter()) { + out.print(xml.toString()); + out.flush(); + } + + } + + @PostMapping("/createDeviceByLine") + @ApiOperation("根据航线生成点位") + public void createDeviceByLine(String filePath,String bayId,String bayName) throws IOException { + try { + List labelList = new ArrayList<>(); + File file = new File(filePath); + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(file); + doc.getDocumentElement().normalize(); + // 查找所有wpml:action元素 + + NodeList actionNodeList = doc.getElementsByTagName("Placemark"); + if (actionNodeList.getLength() > 0) { + // 遍历所有action元素 + for (int i = 0; i < actionNodeList.getLength(); i++) { + Element actionElement = (Element) actionNodeList.item(i); + + // 查找当前action元素下的wpml:actionActuatorFuncParam元素 + NodeList funcParamNodeList = actionElement.getElementsByTagName("wpml:action"); + for (int j = 0; j < funcParamNodeList.getLength(); j++) { + Element funcParamElement = (Element) funcParamNodeList.item(j); + + // 查找wpml:label元素并获取其内容 + NodeList labelNodeList = funcParamElement.getElementsByTagName("wpml:label"); + if (labelNodeList.getLength() > 0) { + String label = labelNodeList.item(0).getTextContent(); + labelList.add(label); + } + } + + } + } + /** + * 变电站id + */ + String stationId = "35c4e466ddd9809445d3f880f94b4a2f"; + + String stationName = "500kV庄周变电站"; + + /** + * 和中台对应资源 ID 保持一致 + */ + String stationCode = "7FE4BB37-F54A-4808-852B-6CAB43DD71B3-00001"; + + /** + * 和中台对应资源 ID 保持一致 + */ + String areaId = "f77421bca31a87c361e87e6ee107c5ed"; + + /** + * 区域全名 + */ + String areaName = "无人机巡检区域"; + + /** + * 和中台对应资源 ID 保持一致 + */ +// String bayId = "753befbf2a10ed3c684f2b4ddd1be8bd"; + + /** + * 间隔全名 + */ +// String bayName = "无人机巡检间隔"; + + /** + * 主设备ID,和中台对应资源 ID 保持一致 + */ + String mainDeviceId = "7f84eebefcf838f9419f433e5e740d68"; + + /** + * 主设备全名 + */ + String mainDeviceName = "无人机巡检主设备"; + + /** + * 主设备类型,字典项,选择主设备列表的时候带过来 + */ + String deviceType = "24"; + + /** + * 和中台对应资源 ID 保持一致 + */ + String componentId = "2ad2ceca3116b07a5f5689d302778f7d"; + + /** + * 部件名称 + */ + String componentName = "无人机巡检部件"; + + /** + * 采集/保存文件类型列表,格式:多个采集文件类型,采用","分隔 + */ + String saveTypeList = "2"; + + /** + * 识别类型列表,格式:多个识别类型,采用","分隔 + */ + String recognitionTypeList = "2"; + + /** + * <1>:=A 相<2>:=B 相<3>:=C 相 多个相位,采用","分隔 + */ + String phase = "1"; + + + /** + * 按位定义数据来源,支持 32 位 1 为有效, 0 为无效,如下:第 0 位:摄像机第 1 位:机器人第 2 位:无人机第 3 位:声纹第 4 位:在线监测区域巡视主机上报上级系统填写 + */ + String dataType = "0x03"; + + /** + * 关联视频编码及预置位,json格式 + */ + String videoPos = "{\"device_code\":\"\",\"device_pos\":\"\",\"robot_code\":\"\",\"robot_pos\":\"\",\"uav_code\":\"\"}"; + + /** + * 0:不进行告警;1:进行告警 + */ + String isAlarm = "0"; + + String deviceClass = "1"; + String pictureAnalysisTypeList = "{\"PictureDefectAnalysisType\":\"yw_gkxfw,yxdgsg,jyz_pl,bmwh,yw_nc\"}"; + log.info("=========labeiList========" + labelList.size()); + List list = new ArrayList<>(); + LocalDateTime now = LocalDateTime.now(); + String prefix = "DJ_"; + String deviceCode = substationDeviceService.selectNextUavDeviceCode(); + int i = NumberUtil.parseInt(deviceCode); + for (String label : labelList) { + if (StrUtil.isBlank(label)) { + continue; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationDevice::getAreaId, areaId).eq(SubstationDevice::getDeviceName, label); + int count = substationDeviceService.count(queryWrapper); + if (count > 0) { + continue; + } + SubstationDevice substationDevice = new SubstationDevice(); + substationDevice.setStationId(stationId); + substationDevice.setStationCode(stationCode); + substationDevice.setStationName(stationName); + substationDevice.setAreaId(areaId); + substationDevice.setAreaName(areaName); + substationDevice.setBayId(bayId); + substationDevice.setBayName(bayName); + substationDevice.setMainDeviceId(mainDeviceId); + substationDevice.setMainDeviceName(mainDeviceName); + substationDevice.setDeviceType(deviceType); + substationDevice.setComponentId(componentId); + substationDevice.setComponentName(componentName); + substationDevice.setSaveTypeList(saveTypeList); + substationDevice.setRecognitionTypeList(recognitionTypeList); + substationDevice.setDeviceName(label); + substationDevice.setIsAlarm(isAlarm); + substationDevice.setVideoPos(videoPos); + substationDevice.setPhase(phase); + substationDevice.setDataType(dataType); + substationDevice.setStationId(stationId); + substationDevice.setLastmodifier("admin"); + substationDevice.setLastmodifydate(now); + substationDevice.setIdentifyMaterialId("0"); + substationDevice.setDeviceClass(deviceClass); + substationDevice.setPictureAnalysisTypeList(pictureAnalysisTypeList); + + String formattedString = String.format("%06d", i); + substationDevice.setDeviceCode(prefix + formattedString); + substationDevice.setDatastatus("1"); + substationDevice.setRecognitionTypeNameList("位置状态识别"); + list.add(substationDevice); + i++; + } + substationDeviceService.saveBatch(list); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationMaindeviceController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationMaindeviceController.java new file mode 100644 index 0000000..17100ca --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationMaindeviceController.java @@ -0,0 +1,462 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationMaindevice; +import com.yfd.platform.modules.basedata.domain.TreeNode; +import com.yfd.platform.modules.basedata.service.ISubstationComponentService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationMaindeviceService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 变电站_主设备 前端控制器 + *

+ * + * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation-maindevice") +@Api(value = "SubstationMaindeviceController", tags = "主设备及部件") +public class SubstationMaindeviceController { + + @Resource + private ISubstationMaindeviceService substationMaindeviceService; + + @Resource + private ISubstationComponentService substationComponentService; + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @GetMapping("/getMainDeviceTree") + @ApiOperation("获取主设备及部件树") + @PreAuthorize("@el.check('select:maindevice')") + public ResponseResult getMainDeviceTree(String bayId, String name) { + List> mainDeviceTree = substationMaindeviceService.getMainDeviceTree(bayId, name); + return ResponseResult.successData(mainDeviceTree); + } + + @Log(module = "主设备及部件", value = "新增主设备",type = "1") + @PostMapping("/addMainDevice") + @ApiOperation("新增主设备") + @PreAuthorize("@el.check('add:maindevice')") + public ResponseResult addMainDevice(SubstationMaindevice substationMaindevice) { + String mainDeviceName = substationMaindevice.getMainDeviceName(); + String bayId = substationMaindevice.getBayId(); + int count = + substationMaindeviceService.count(new LambdaQueryWrapper().eq(SubstationMaindevice::getBayId, bayId).eq(SubstationMaindevice::getMainDeviceName, mainDeviceName)); + if (count > 0) { + return ResponseResult.error("当前间隔下已经存在该主设备"); + } + int count1 = + substationMaindeviceService.count(new LambdaQueryWrapper().eq(SubstationMaindevice::getBayId, bayId)) + 1; + substationMaindevice.setCustom1(count1); + substationMaindevice.setDatastatus("1"); + substationMaindevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationMaindevice.setLastmodifydate(LocalDateTime.now()); + boolean ok = substationMaindeviceService.save(substationMaindevice); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @GetMapping("/getMainDeviceById") + @ApiOperation("根据Id获取主设备") + public ResponseResult getMainDeviceById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + SubstationMaindevice substationMaindevice = substationMaindeviceService.getById(id); + return ResponseResult.successData(substationMaindevice); + } + + @GetMapping("/getMainDevice") + @ApiOperation("根据条件获取主设备") + public ResponseResult getMainDevice(String stationCode, String areaId, String bayId, String mainDeviceName, + String deviceType) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.like(SubstationMaindevice::getStationCode, stationCode); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationMaindevice::getAreaId, areaId); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.like(SubstationMaindevice::getBayId, bayId); + } + // 主设备名称 + if (StrUtil.isNotBlank(mainDeviceName)) { + queryWrapper.like(SubstationMaindevice::getMainDeviceName, mainDeviceName); + } + // 主设备类型 + if (StrUtil.isNotBlank(deviceType)) { + queryWrapper.eq(SubstationMaindevice::getDeviceType, deviceType); + } + queryWrapper.eq(SubstationMaindevice::getDatastatus, "1").orderByAsc(SubstationMaindevice::getCustom1); + List> list = substationMaindeviceService.listMaps(queryWrapper); + return ResponseResult.successData(list); + } + + @PostMapping("/getMainDevicePage") + @ApiOperation("根据条件获取主设备(分页)") + public ResponseResult getMainDevicePage(@RequestBody String param) { + JSONObject jsonObject = JSONUtil.parseObj(param); + Page> page = new Page<>(); + page.setCurrent(jsonObject.getInt("current")); + page.setSize(jsonObject.getInt("size")); + String stationCode = jsonObject.getStr("stationCode"); + String areaId = jsonObject.getStr("areaId"); + String bayId = jsonObject.getStr("bayId"); + String mainDeviceName = jsonObject.getStr("mainDeviceName"); + String deviceType = jsonObject.getStr("deviceType"); + String ids = jsonObject.getStr("ids"); + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.like(SubstationMaindevice::getStationCode, stationCode); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationMaindevice::getAreaId, areaId); + } + if (StrUtil.isNotBlank(ids)) { + String[] split = ids.split(","); + queryWrapper.notIn(SubstationMaindevice::getMainDeviceId, Arrays.asList(split)); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.like(SubstationMaindevice::getBayId, bayId); + } + // 主设备名称 + if (StrUtil.isNotBlank(mainDeviceName)) { + queryWrapper.like(SubstationMaindevice::getMainDeviceName, mainDeviceName); + } + // 主设备类型 + if (StrUtil.isNotBlank(deviceType)) { + queryWrapper.eq(SubstationMaindevice::getDeviceType, deviceType); + } + queryWrapper.eq(SubstationMaindevice::getDatastatus, "1"); + Page> mapPage = substationMaindeviceService.pageMaps(page, queryWrapper); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getComponentByBay") + @ApiOperation("根据条件获取部件") + public ResponseResult getComponentByBay(String stationCode, String areaId, String bayId, String componentName, + String mainDeviceId) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.eq(SubstationComponent::getStationCode, stationCode); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationComponent::getAreaId, areaId); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.eq(SubstationComponent::getBayId, bayId); + } + // 部件名称 + if (StrUtil.isNotBlank(componentName)) { + queryWrapper.like(SubstationComponent::getComponentName, componentName); + } + // 主设备类型 + if (StrUtil.isNotBlank(mainDeviceId)) { + queryWrapper.eq(SubstationComponent::getMainDeviceId, mainDeviceId); + } + queryWrapper.eq(SubstationComponent::getDatastatus, "1").orderByAsc(SubstationComponent::getCustom1); + List> list = substationComponentService.listMaps(queryWrapper); + return ResponseResult.successData(list); + } + + @PostMapping("/getComponentByBayPage") + @ApiOperation("根据条件获取部件(分页)") + public ResponseResult getComponentByBayPage(@RequestBody String param) { + +// Page> page, String stationCode, String areaId, +// String bayId, String componentName, String mainDeviceId, String ids + JSONObject jsonObject = JSONUtil.parseObj(param); + Page> page = new Page<>(); + page.setCurrent(jsonObject.getInt("current")); + page.setSize(jsonObject.getInt("size")); + String stationCode = jsonObject.getStr("stationCode"); + String areaId = jsonObject.getStr("areaId"); + String bayId = jsonObject.getStr("bayId"); + String componentName = jsonObject.getStr("componentName"); + String mainDeviceId = jsonObject.getStr("mainDeviceId"); + String ids = jsonObject.getStr("ids"); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.like(SubstationComponent::getStationCode, stationCode); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.like(SubstationComponent::getAreaId, areaId); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.like(SubstationComponent::getBayId, bayId); + } + // 部件名称 + if (StrUtil.isNotBlank(componentName)) { + queryWrapper.like(SubstationComponent::getComponentName, componentName); + } + if (StrUtil.isNotBlank(ids)) { + String[] split = ids.split(","); + queryWrapper.notIn(SubstationComponent::getComponentId, Arrays.asList(split)); + } + // 主设备类型 + if (StrUtil.isNotBlank(mainDeviceId)) { + queryWrapper.eq(SubstationComponent::getMainDeviceId, mainDeviceId); + } + queryWrapper.eq(SubstationComponent::getDatastatus, "1"); + Page> mapPage = substationComponentService.pageMaps(page, queryWrapper); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getMainDeviceType") + @ApiOperation("获取当前主设备类型") + public ResponseResult getMainDeviceType(String deviceType) { + SysDictionaryItems sysDictionaryItems = substationMaindeviceService.getMainDeviceType(deviceType); + return ResponseResult.successData(sysDictionaryItems); + } + + @GetMapping("/getComponent") + @ApiOperation("获取部件") + public ResponseResult getComponent(String mainDeviceId,String bayId,String componentName) { + List> list = substationMaindeviceService.getComponent(mainDeviceId,bayId,componentName); + return ResponseResult.successData(list); + } + + @GetMapping("/getComponentByBayId") + @ApiOperation("获取间隔下的部件") + public ResponseResult getComponentByBayId(String bayId) { + List> list = substationMaindeviceService.getComponentByBay(bayId); + return ResponseResult.successData(list); + } + + @GetMapping("/getDeviceType") + @ApiOperation("获取主设备类型集合") + public ResponseResult getDeviceType() { + List> listItems = substationMaindeviceService.getDeviceType(); + return ResponseResult.successData(listItems); + } + + @Log(module = "主设备及部件", value = "修改主设备",type = "1") + @PostMapping("/updateMainDevice") + @ApiOperation("修改主设备") + @PreAuthorize("@el.check('update:maindevice')") + public ResponseResult updateMainDevice(SubstationMaindevice substationMaindevice) { + if (substationMaindevice == null) { + return ResponseResult.error("参数为空"); + } + String mainDeviceId = substationMaindevice.getMainDeviceId(); + String bayId = substationMaindevice.getBayId(); + String mainDeviceName = substationMaindevice.getMainDeviceName(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationMaindevice::getBayId, bayId).eq(SubstationMaindevice::getMainDeviceName, + mainDeviceName).ne(SubstationMaindevice::getMainDeviceId, mainDeviceId); + int count = substationMaindeviceService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前间隔下已经存在该主设备"); + } + int count2 = + substationDeviceService.count(new LambdaQueryWrapper().eq(SubstationDevice::getMainDeviceId, mainDeviceId)); + if (count2 > 0) { + return ResponseResult.error("当前主设备有点位关联不能修改"); + } + substationMaindevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationMaindevice.setLastmodifydate(LocalDateTime.now()); + boolean ok = substationMaindeviceService.updateById(substationMaindevice); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @Log(module = "主设备及部件", value = "修改主设备状态",type = "1") + @PostMapping("/setMainDeviceStatus") + @ApiOperation("修改主设备状态") + public ResponseResult setMainDeviceStatus(String id, String dataStatus) { + boolean ok = substationMaindeviceService.setMainDeviceStatus(id, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + + } + + @Log(module = "主设备及部件", value = "删除主设备",type = "1") + @PostMapping("/deleteMainDevice") + @ApiOperation("删除主设备") + @PreAuthorize("@el.check('del:maindevice')") + public ResponseResult deleteMainDevice(String id) { + boolean ok = substationMaindeviceService.deleteMainDevice(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "主设备及部件", value = "新增部件",type = "1") + @PostMapping("/addComponent") + @ApiOperation("新增部件") + public ResponseResult addComponent(SubstationComponent substationComponent) { + String mainDeviceId = substationComponent.getMainDeviceId(); + String componentName = substationComponent.getComponentName(); + int count = + substationComponentService.count(new LambdaQueryWrapper().eq(SubstationComponent::getMainDeviceId, mainDeviceId).eq(SubstationComponent::getComponentName, componentName)); + if (count > 0) { + return ResponseResult.error("当前主设备下已经存在该部件"); + } + int count1 = + substationComponentService.count(new LambdaQueryWrapper().eq(SubstationComponent::getBayId, mainDeviceId)) + 1; + substationComponent.setCustom1(count1); + boolean ok = substationMaindeviceService.addComponent(substationComponent); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @GetMapping("/getComponentById") + @ApiOperation("根据Id获取部件") + public ResponseResult getComponentById(String componentId) { + SubstationComponent substationComponent = substationMaindeviceService.getComponentById(componentId); + return ResponseResult.successData(substationComponent); + } + + @Log(module = "主设备及部件", value = "修改部件",type = "1") + @PostMapping("/updateComponent") + @ApiOperation("修改部件") + public ResponseResult updateComponent(SubstationComponent substationComponent) { + String mainDeviceId = substationComponent.getMainDeviceId(); + String componentName = substationComponent.getComponentName(); + String componentId = substationComponent.getComponentId(); + int count = + substationComponentService.count(new LambdaQueryWrapper().eq(SubstationComponent::getMainDeviceId, mainDeviceId).eq(SubstationComponent::getComponentName, componentName).ne(SubstationComponent::getComponentId, componentId)); + if (count > 0) { + return ResponseResult.error("当前主设备下已经存在该部件"); + } + int count2 = + substationDeviceService.count(new LambdaQueryWrapper().eq(SubstationDevice::getComponentId, componentId)); + if (count2 > 0) { + return ResponseResult.error("当前部件有点位关联不能修改"); + } + boolean ok = substationMaindeviceService.updateComponent(substationComponent); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "主设备及部件", value = "修改部件状态",type = "1") + @PostMapping("/setComponentStatus") + @ApiOperation("修改部件状态") + public ResponseResult setComponentStatus(String componentId, String dataStatus) { + boolean ok = substationMaindeviceService.setComponentStatus(componentId, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "主设备及部件", value = "删除部件",type = "1") + @PostMapping("/deleteComponent") + @ApiOperation("删除部件") + public ResponseResult deleteComponent(String componentId) { + boolean ok = substationMaindeviceService.deleteComponent(componentId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @PostMapping("/uploadFile") + @ApiOperation("导入文件") + public ResponseResult uploadFile(MultipartFile file, String type) throws Exception { + String str = substationMaindeviceService.uploadExcel(file, type); + if (StrUtil.isBlank(str)) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @GetMapping("/getMainDeviceNumber") + @ApiOperation("查询主设备个数") + public ResponseResult getMainDeviceNumber(String stationCode) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationMaindevice::getStationCode, stationCode).eq(SubstationMaindevice::getDatastatus, "1"); + List list = substationMaindeviceService.list(queryWrapper); + Map> collect = + list.stream().filter(s -> StrUtil.isNotBlank(s.getDeviceType())).collect(Collectors.groupingBy(SubstationMaindevice::getDeviceType)); + Map mainDevice = new HashMap<>(); + List> equipmentType = sysDictionaryItemsService.getDeviceByType("EquipmentType"); + String[] mainArray = {"1", "2", "3", "4", "6", "7", "8", "9", "10"}; + List mainList = Arrays.asList(mainArray); + for (Map map : equipmentType) { + String itemcode = map.get("itemcode").toString(); + if (!mainList.contains(itemcode)) { + continue; + } + int count = 0; + for (String type : collect.keySet()) { + List substationMaindevices = collect.get(type); + if (type.equals(itemcode)) { + count = substationMaindevices.size(); + break; + } + } + String custom2 = map.get("custom2").toString(); + mainDevice.put(custom2, count); + } + return ResponseResult.successData(mainDevice); + } + + @ApiOperation("获取主设备导航树") + @GetMapping("/getSubstationMainDeviceTree") + public ResponseResult getSubstationMainDeviceTree(String stationCode,String mainDeviceName) { + List> mainDeviceTree = substationMaindeviceService.getSubstationMainDeviceTree(stationCode,mainDeviceName); + return ResponseResult.successData(mainDeviceTree); + } + + @GetMapping("/getSubstationMainTree") + @ApiOperation("获取主设备树(前台)") + public ResponseResult getSubstationMainTree(String stationCode, String mainDeviceName) { + TreeNode treeNode = substationMaindeviceService.getSubstationMainTree(stationCode, mainDeviceName); + return ResponseResult.successData(treeNode); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationModelController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationModelController.java new file mode 100644 index 0000000..c075fad --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationModelController.java @@ -0,0 +1,192 @@ +package com.yfd.platform.modules.basedata.controller; + + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationModel; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.SubstationPatroldeviceMapper; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationModelService; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 变电站-三维动态模型 前端控制器 + *

+ * + * @author zhengsl + * @since 2024-04-05 + */ +@RestController +@RequestMapping("/basedata/substation-model") +@Api(value = "SubstationModelController", tags = "变电站关联三维模型") +public class SubstationModelController { + @Resource + private ISubstationModelService substationModelService; + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private SubstationPatroldeviceMapper patroldeviceMapper; + + @GetMapping("/getModelListByType") + @ApiOperation("根据类型查询模型对象列表") + public ResponseResult getModelListByType(String stationid,String type) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationModel::getStationId,stationid); + queryWrapper.eq(SubstationModel::getType, type); + queryWrapper.orderByAsc(SubstationModel::getObjname); + List> listMaps = substationModelService.listMaps(queryWrapper); + return ResponseResult.successData(listMaps); + } + + + @PostMapping("/addModelObject") + @ApiOperation("新增模型对象") + public ResponseResult addModelObject(@RequestBody SubstationModel substationModel) { + + boolean ok = substationModelService.save(substationModel); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @PostMapping("/updateModelObject") + @ApiOperation("修改模型对象") + public ResponseResult updateModelObject(@RequestBody SubstationModel substationModel) { + boolean ok = false; + if(StrUtil.isNotEmpty(substationModel.getId())){ + ok=substationModelService.updateById(substationModel); + } + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @PostMapping("/deleteModelObject") + @ApiOperation("删除模型对象") + public ResponseResult deleteModelObject(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("模型对象ID为空"); + } + boolean ok = substationModelService.removeById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除模型对象"); + } + } + + @PostMapping("/setModelLine") + @ApiOperation("设置航线点位坐标") + public ResponseResult deleteModelObject(String id,String type, String index, String x, String y, String z) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("id为空"); + } + SubstationModel substationModel = substationModelService.getById(id); + JSONArray modelArray = new JSONArray(); + String linePoints = substationModel.getLinePoints(); + JSONArray jsonArray = JSONUtil.parseArray(linePoints); + List split = StrUtil.split(index, ","); + List indexList = split.stream().map(NumberUtil::parseInt).collect(Collectors.toList()); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + if (indexList.contains(i)) { + if (StrUtil.isNotBlank(x)) { + jsonObject.putOpt("x", NumberUtil.parseInt(x)); + } + if (StrUtil.isNotBlank(y)) { + jsonObject.putOpt("y", NumberUtil.parseInt(y)); + } + if (StrUtil.isNotBlank(z)) { + jsonObject.putOpt("z", NumberUtil.parseInt(z)); + } + + } + modelArray.add(jsonObject); + } + JSONArray modelArray1 = new JSONArray(); + String checkPoints = substationModel.getCheckPoints(); + JSONArray jsonArray1 = JSONUtil.parseArray(checkPoints); + for (int i = 0; i < jsonArray1.size(); i++) { + JSONObject jsonObject1 = jsonArray1.getJSONObject(i); + if (indexList.contains(i)) { + if (StrUtil.isNotBlank(x)) { + jsonObject1.putOpt("x", NumberUtil.parseInt(x)); + } + if (StrUtil.isNotBlank(y)) { + jsonObject1.putOpt("y", NumberUtil.parseInt(y)); + } + if (StrUtil.isNotBlank(z)) { + jsonObject1.putOpt("z", NumberUtil.parseInt(z)); + } + + } + modelArray1.add(jsonObject1); + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SubstationModel::getId, id).set(SubstationModel::getLinePoints, modelArray.toString()).set(SubstationModel::getCheckPoints, modelArray1.toString()); + substationModelService.update(updateWrapper); + return ResponseResult.success(); + } + + @GetMapping("/getDevicelListByTask") + @ApiOperation("根据任务查询任务对应的点位信息") + public ResponseResult getDevicelListByTask(String stationid, String tasktodoid, String robotCode) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskResult::getTaskTodoId, tasktodoid); + queryWrapper.select(TaskResult::getDeviceId); + queryWrapper.orderByAsc(TaskResult::getOrderNum); + List results = taskResultMapper.selectList(queryWrapper); + List> listMaps = new ArrayList<>(); + for (int i = 0; i < results.size(); i++) { + TaskResult result = results.get(i); + LambdaQueryWrapper modelWrapper = new LambdaQueryWrapper<>(); + modelWrapper.eq(SubstationModel::getStationId, stationid); + modelWrapper.eq(SubstationModel::getType, "02"); + modelWrapper.like(SubstationModel::getObjinfo,result.getDeviceId()); + List> list = substationModelService.listMaps(modelWrapper); + if(list.size()>0){ + listMaps.add(list.get(0)); + } + } + + LambdaQueryWrapper patrolqueryWrapper = new LambdaQueryWrapper<>(); + patrolqueryWrapper.eq(SubstationPatroldevice::getRobotsCode, robotCode); + patrolqueryWrapper.select(SubstationPatroldevice::getPatroldeviceId); + List patroldevices=patroldeviceMapper.selectList(patrolqueryWrapper); + if(patroldevices.size()>0){ + String patroldeviceid=patroldevices.get(0).getPatroldeviceId(); + LambdaQueryWrapper modelWrapper1 = new LambdaQueryWrapper<>(); + modelWrapper1.eq(SubstationModel::getStationId,stationid); + modelWrapper1.in(SubstationModel::getType, "04","06"); + modelWrapper1.like(SubstationModel::getObjinfo,patroldeviceid); + List> robots = substationModelService.listMaps(modelWrapper1); + if(robots.size()>0){ + listMaps.add(robots.get(0)); + } + } + return ResponseResult.successData(listMaps); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationPatroldeviceController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationPatroldeviceController.java new file mode 100644 index 0000000..9837aa0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/SubstationPatroldeviceController.java @@ -0,0 +1,1296 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.*; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; +import com.yfd.platform.modules.basedata.domain.PatroldeviceResume; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.IDeviceChannelService; +import com.yfd.platform.modules.basedata.service.IPatroldeviceResumeService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.deviceapi.CameraUtil; +import com.yfd.platform.modules.robotapi.service.IRobotOfflineLogService; +import com.yfd.platform.system.domain.RolePatroldevice; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.IRolePatroldeviceService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + *

+ * 变电站_巡视设备 前端控制器 + *

+ * + * + * @since 2023-03-28 + */ +@RestController +@RequestMapping("/basedata/substation-patroldevice") +@Api(value = "SubstationPatroldeviceController", tags = "巡视设备") +@Slf4j +public class SubstationPatroldeviceController { + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @Resource + private ISubstationDeviceService substationDeviceService; + + + @Resource + private Map>> typeList; + + @Resource + private IDeviceChannelService deviceChannelService; + + @Resource + private IUserService userService; + + @Resource + private IPatroldeviceResumeService patroldeviceResumeService; + + @Resource + private IRolePatroldeviceService rolePatroldeviceService; + @Resource + private HttpServerConfig config; + @Resource + private HttpRESTfulUtils httpUtil; + @Resource + private IRobotOfflineLogService robotOfflineLogService; + + @Value("${file-space.system}") + private String systempath; + + @PostMapping("/getPatrolDeviceList") + @ApiOperation("分页查询巡视设备") + @PreAuthorize("@el.check('select:patroldevice')") + public ResponseResult getPatrolDeviceList(@RequestBody String param) { + JSONObject jsonObject = JSONUtil.parseObj(param); + Page> page = new Page<>(); + page.setCurrent(jsonObject.getInt("current")); + page.setSize(jsonObject.getInt("size")); + String stationId = jsonObject.getStr("stationId"); + String areaId = jsonObject.getStr("areaId"); + String patrolDeviceName = jsonObject.getStr("patrolDeviceName"); + String type = jsonObject.getStr("type"); + String deviceModel = jsonObject.getStr("deviceModel"); + String ids = jsonObject.getStr("ids"); + String manufacturer = jsonObject.getStr("manufacturer"); + String flag = jsonObject.getStr("flag"); + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (usertype != 0 && ("10".equals(type) || StrUtil.isBlank(type))) { + //if (usertype != 0 && !"14".equals(type)) { + // 非管理员只可以查看指定摄像头 + List cameraIds = substationPatroldeviceService.getCameraIds(userInfo.getId()); + // 获取用户所有的摄像头 + if (cameraIds.size() > 0) { + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, cameraIds); + } else { + return ResponseResult.successData(null); + } + } + if ("1".equals(flag)) { + queryWrapper.and(i -> i.isNull(SubstationPatroldevice::getStationId).or().eq(SubstationPatroldevice::getStationId, "")); + } else { + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationPatroldevice::getAreaId, areaId); + } + } + + // 模糊查询(设备名称) + if (StrUtil.isNotBlank(patrolDeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patrolDeviceName); + } + // 模糊查询(设备型号) + if (StrUtil.isNotBlank(deviceModel)) { + queryWrapper.like(SubstationPatroldevice::getDeviceModel, deviceModel); + } + + // 模糊查询(生产厂家) + if (StrUtil.isNotBlank(manufacturer)) { + queryWrapper.like(SubstationPatroldevice::getManufacturer, manufacturer); + } + if (StrUtil.isNotBlank(ids)) { + String[] split = ids.split(","); + queryWrapper.notIn(SubstationPatroldevice::getPatroldeviceId, Arrays.asList(split)); + } + // 类型 + if (StrUtil.isNotBlank(type)) { + List typeList = Arrays.asList(type.split(",")); + queryWrapper.in(SubstationPatroldevice::getType, typeList); + if ("10".equals(type) || "11".equals(type)) { + queryWrapper.and(i -> i.isNull(SubstationPatroldevice::getRobotsCode).or().eq(SubstationPatroldevice::getRobotsCode, "")); + } + } else { + String[] code = {"10", "11"}; + List codeList = Arrays.asList(code); + // queryWrapper.in(SubstationPatroldevice::getType, codeList).and(i -> i.isNull(SubstationPatroldevice::getRobotsCode).or().eq(SubstationPatroldevice::getRobotsCode, "")); + queryWrapper.in(SubstationPatroldevice::getType, codeList).and(i -> i.isNull(SubstationPatroldevice::getRobotsCode).or().eq(SubstationPatroldevice::getRobotsCode, "")); + } + queryWrapper.orderByAsc(SubstationPatroldevice::getPatroldeviceCode); + Page> mapPage = substationPatroldeviceService.pageMaps(page, queryWrapper); + List> records = page.getRecords(); + List> patrolDevices = new ArrayList<>(); + String[] code = {"1", "2", "3", "13"}; + List codeList = Arrays.asList(code); + for (Map record : records) { + record.remove("lastmodifydate"); + String type2 = (String) record.get("type"); + String patroldeviceCode = null; + if (codeList.contains(type2)) { + record.put("patrolDeviceOnline", record.get("online")); + } else { + patroldeviceCode = record.get("internationalId").toString(); + } + + if (ObjUtil.isNotEmpty(record.get("channelinfo")) && JSONUtil.isTypeJSONArray(record.get("channelinfo").toString())) { + JSONArray channels = JSONUtil.parseArray(record.get("channelinfo").toString()); + List> mapList = new ArrayList<>(); + for (int i = 0; i < channels.size(); i++) { + JSONObject channel = channels.getJSONObject(i); + Map map = new HashMap<>(); + map.put("channelType", channel.getStr("channel_type")); + // 1:摄像机 + if ("1".equals(channel.getStr("from_device"))) { + map.put("deviceId", record.get("internationalId").toString()); + map.put("channelId", channel.getStr("channel_code")); + } else { + // 2:来源录像机 + map.put("deviceId", channel.getStr("recorder_code")); + map.put("channelId", channel.getStr("recorder_channelcode")); + } + mapList.add(map); + } + record.put("channelList", mapList); + } else { + //没有配置 + LambdaQueryWrapper deviceChannelLambdaQueryWrapper = new LambdaQueryWrapper<>(); + deviceChannelLambdaQueryWrapper.eq(DeviceChannel::getDeviceid, patroldeviceCode); + deviceChannelLambdaQueryWrapper.select(DeviceChannel::getDeviceid, DeviceChannel::getChannelid); + List> mapList = deviceChannelService.listMaps(deviceChannelLambdaQueryWrapper); + if (mapList != null && mapList.size() > 0) { + record.put("channelList", mapList); + } + } + Map>> deviceType = typeList; + // 巡视设备类型 + List> list = deviceType.get("PatrolEquipmentType"); + for (Map map : list) { + String itemcode = (String) map.get("itemcode"); + String type1 = (String) record.get("type"); + if (itemcode.equals(type1)) { + record.put("typeName", map.get("dictname")); + break; + } + } + patrolDevices.add(record); + } + mapPage.setRecords(patrolDevices); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getPatrolCameraList") + @ApiOperation("获取联动信号摄像机") + public ResponseResult getPatrolCameraList(String stationId, String areaId, String patrolDeviceName, String type) { + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (usertype != 0) { + //if (usertype != 0 && !"14".equals(type)) { + // 非管理员只可以查看指定摄像头 + List ids = substationPatroldeviceService.getCameraIds(userInfo.getId()); + // 获取用户所有的摄像头 + if (ids.size() > 0) { + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, ids); + } else { + return ResponseResult.successData(null); + } + } + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationPatroldevice::getAreaId, areaId); + } + // 模糊查询(设备名称) + if (StrUtil.isNotBlank(patrolDeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patrolDeviceName); + } + // 类型 + if (StrUtil.isNotBlank(type)) { + queryWrapper.eq(SubstationPatroldevice::getType, type); + } + queryWrapper.eq(SubstationPatroldevice::getDatastatus, "1").eq(SubstationPatroldevice::getType, "10")//摄像机 + .orderByAsc(SubstationPatroldevice::getType); + List> maps = substationPatroldeviceService.listMaps(queryWrapper); + List> patrolDevices = new ArrayList<>(); + for (Map record : maps) { + if (ObjUtil.isNotEmpty(record.get("channelinfo")) && JSONUtil.isTypeJSONArray(record.get("channelinfo").toString())) { + JSONArray channels = JSONUtil.parseArray(record.get("channelinfo").toString()); + for (int i = 0; i < channels.size(); i++) { + JSONObject channel = channels.getJSONObject(i); + Map copyMap = new HashMap<>(record); + copyMap.put("channelType", channel.getStr("channel_type")); + if ("2".equals(channel.getStr("channel_type"))) { + copyMap.put("patroldeviceName", record.get("patroldeviceName").toString() + "_红外"); + } + // 1:摄像机 + if ("1".equals(channel.getStr("from_device"))) { + copyMap.put("deviceId", record.get("internationalId").toString()); + copyMap.put("channelId", channel.getStr("channel_code")); + } else { + // 2:来源录像机 + copyMap.put("deviceId", channel.getStr("recorder_code")); + copyMap.put("channelId", channel.getStr("recorder_channelcode")); + } + //如果指定了视频流地址,则优先取指定的地址 + copyMap.put("channelRtspurl", channel.getStr("channel_rtspurl")); + patrolDevices.add(copyMap); + } + } else { + //没有配置 + String patroldeviceCode = record.get("internationalId").toString(); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(DeviceChannel::getDeviceid, patroldeviceCode).eq(DeviceChannel::getAddress, "Address") + .select(DeviceChannel::getDeviceid, + DeviceChannel::getChannelid) + .orderByAsc(DeviceChannel::getChannelid); + List> channels = deviceChannelService.listMaps(queryWrapper1); + for (int i = 0; i < channels.size(); i++) { + Map channel = channels.get(i); + Map copyMap = new HashMap<>(record); + copyMap.put("patroldeviceName", record.get("patroldeviceName").toString() + "_" + (i + 1)); + copyMap.put("deviceId", patroldeviceCode); + copyMap.put("channelId", channel.get("channelId").toString()); + patrolDevices.add(copyMap); + } + } + + } + return ResponseResult.successData(patrolDevices); + } + + @GetMapping("/getCameraList") + @ApiOperation("获取摄像头") + public ResponseResult getCameraList(String stationId, String patroldeviceName, String type) { + String[] code = {"11", "12", "15", "20", "21"}; + List codeList = Arrays.asList(code); + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + // 根据用户id查询所有摄像头id + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (usertype != 0) { + // 非管理员只可以查看指定摄像头 + List ids = substationPatroldeviceService.getCameraIds(userInfo.getId()); + // 获取用户所有的摄像头 + if (ids.size() > 0) { + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, ids); + } else { + return ResponseResult.successData(null); + } + } + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + if (StrUtil.isNotBlank(patroldeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patroldeviceName); + } + if (StrUtil.isNotBlank(type)) { + queryWrapper.eq(SubstationPatroldevice::getType, type); + } else { + queryWrapper.notIn(SubstationPatroldevice::getType, codeList); + } + // queryWrapper.and(i -> i.isNull(SubstationPatroldevice::getRobotsCode).or().eq(SubstationPatroldevice::getRobotsCode, "")); + queryWrapper.eq(SubstationPatroldevice::getDatastatus, "1"); + queryWrapper.select(SubstationPatroldevice::getPatroldeviceId, SubstationPatroldevice::getAreaId,SubstationPatroldevice::getInternationalId, + SubstationPatroldevice::getAreaName, SubstationPatroldevice::getPatroldeviceName, + SubstationPatroldevice::getPatroldeviceCode, SubstationPatroldevice::getPlace, + SubstationPatroldevice::getType,SubstationPatroldevice::getVideoMode, SubstationPatroldevice::getUseMode, SubstationPatroldevice::getOnline, SubstationPatroldevice::getChannelinfo); + List> list = substationPatroldeviceService.listMaps(queryWrapper); + List> cameraList = new ArrayList<>(); + Map>> deviceType = substationDeviceService.getDeviceType(); + List> list1 = deviceType.get("PatrolEquipmentType"); + // 机器人无人机类型 + for (Map map : list) { + String type1 = (String) map.get("type"); + for (Map stringObjectMap : list1) { + String itemcode = (String) stringObjectMap.get("itemcode"); + if (type1.equals(itemcode)) { + map.put("typeName", stringObjectMap.get("dictname")); + break; + } + } + + //郑顺利2023-08-27修改,不从DeviceChannel 获取摄像头信息,修改为从摄像机对应channelinfo中获取通道信息 + // channelinfo=[{"channel_type":"2","channel_code":"","channel_rtspurl":"","from_device":"1","recorder_code":"","recorder_channelcode":""}] + // channel_type: 1=可见光,2=红外 "from_device":1=摄像机 2=硬盘录像机 + if("15".equals(type)){ + map.put("deviceId", map.get("internationalId").toString()); + map.put("channelId", map.get("internationalId").toString()); + cameraList.add(map); + } + if (ObjUtil.isNotEmpty(map.get("channelinfo")) && JSONUtil.isTypeJSONArray(map.get("channelinfo").toString())) { + JSONArray channels = JSONUtil.parseArray(map.get("channelinfo").toString()); + for (int i = 0; i < channels.size(); i++) { + JSONObject channel = channels.getJSONObject(i); + Map copyMap = new HashMap<>(map); + copyMap.put("channelType", channel.getStr("channel_type")); + if ("2".equals(channel.getStr("channel_type"))) { + copyMap.put("patroldeviceName", map.get("patroldeviceName").toString() + "_红外"); + } + // 1:摄像机 + if ("1".equals(channel.getStr("from_device"))) { + copyMap.put("deviceId", map.get("internationalId").toString()); + copyMap.put("channelId", channel.getStr("channel_code")); + } else { + // 2:来源录像机 + copyMap.put("deviceId", channel.getStr("recorder_code")); + copyMap.put("channelId", channel.getStr("recorder_channelcode")); + } + //如果指定了视频流地址,则优先取指定的地址 + copyMap.put("channelRtspurl", channel.getStr("channel_rtspurl")); + cameraList.add(copyMap); + } + } else { + //没有配置 + String patroldeviceCode = map.get("internationalId").toString(); //修改为取国标码 + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(DeviceChannel::getDeviceid, patroldeviceCode).eq(DeviceChannel::getAddress, "Address") + .select(DeviceChannel::getDeviceid, + DeviceChannel::getChannelid) + .orderByAsc(DeviceChannel::getChannelid); + List> channels = deviceChannelService.listMaps(queryWrapper1); + for (int i = 0; i < channels.size(); i++) { + Map channel = channels.get(i); + Map copyMap = new HashMap<>(map); + String patroldeviceName1 = ObjectUtil.isEmpty(map.get("patroldeviceName")) ? "" : map.get("patroldeviceName").toString(); + copyMap.put("patroldeviceName", patroldeviceName1 + "_" + (i + 1)); + copyMap.put("deviceId", patroldeviceCode); + copyMap.put("channelId", channel.get("channelId").toString()); + cameraList.add(copyMap); + } + } + } + return ResponseResult.successData(cameraList); + } + + @GetMapping("/getRobotList") + @ApiOperation("获取机器人无人机") + public ResponseResult getRobotList(String stationId, String patroldeviceName, String type) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + if (StrUtil.isNotBlank(patroldeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patroldeviceName); + } + if (StrUtil.isNotBlank(type)) { + queryWrapper.in(SubstationPatroldevice::getType, Arrays.asList(type.split(","))); + } + List> mapList = substationPatroldeviceService.listMaps(queryWrapper); + String[] code = {"1", "2", "3", "13"}; + List codeList = Arrays.asList(code); + mapList.forEach(record -> { + String type2 = (String) record.get("type"); + if (codeList.contains(type2)) { + record.put("patrolDeviceOnline", record.get("online")); + } + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(SubstationPatroldevice::getRobotsCode, record.get("patroldeviceCode").toString()); + List> list = substationPatroldeviceService.listMaps(queryWrapper1); + list.forEach(map -> { + LambdaQueryWrapper deviceChannelLambdaQueryWrapper = new LambdaQueryWrapper<>(); + deviceChannelLambdaQueryWrapper.eq(DeviceChannel::getDeviceid, map.get("internationalId").toString()); + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceChannelLambdaQueryWrapper); + if (deviceChannel != null) { + map.put("deviceId", deviceChannel.getDeviceid()); + map.put("channelId", deviceChannel.getChannelid()); + } else { + map.put("deviceId", map.get("internationalId").toString()); + map.put("channelId", map.get("internationalId").toString()); + } + }); + + if (ObjectUtil.isEmpty(record.get("channelList"))) { + record.put("channelList", list); + } + }); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getCameraByRobot") + @ApiOperation("根据机器人编码获取摄像头") + public ResponseResult getCameraByRobot(String robotsCode) { + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getRobotsCode, robotsCode)); + return ResponseResult.successData(list); + } + + @GetMapping("/getPatrolDeviceByType") + @ApiOperation("根据类型获取巡视设备") + public ResponseResult getPatrolDeviceByType(Page> page, String stationId, String type, + String patrolDeviceName, String ids) { + if (StrUtil.isBlank(type)) { + return ResponseResult.error("参数错误"); + } + String[] split = type.split(","); + List stringList = Arrays.asList(split); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId).eq(SubstationPatroldevice::getDatastatus, "1" + ).in(SubstationPatroldevice::getType, stringList); + if (StrUtil.isNotBlank(patrolDeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patrolDeviceName); + } + if (StrUtil.isNotBlank(ids)) { + String[] split1 = ids.split(","); + queryWrapper.notIn(SubstationPatroldevice::getPatroldeviceId, Arrays.asList(split1)); + } + Page> mapPage = substationPatroldeviceService.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + String[] code = {"1", "2", "3", "4", "13"}; + List codeList = Arrays.asList(code); + records.forEach(r -> { + String type2 = (String) r.get("type"); + String patroldeviceCode = null; + if (codeList.contains(type2)) { + List> substationPatroldevices = substationPatroldeviceService.listMaps(new LambdaQueryWrapper().eq(SubstationPatroldevice::getRobotsCode, r.get("patroldeviceCode").toString()).eq(SubstationPatroldevice::getDatastatus, "1")); + substationPatroldevices.forEach(map -> { + LambdaQueryWrapper deviceChannelLambdaQueryWrapper = new LambdaQueryWrapper<>(); + deviceChannelLambdaQueryWrapper.eq(DeviceChannel::getDeviceid, map.get("internationalId").toString()); + DeviceChannel deviceChannel = deviceChannelService.getOne(deviceChannelLambdaQueryWrapper); + if (deviceChannel != null) { + map.put("deviceId", deviceChannel.getDeviceid()); + map.put("channelId", deviceChannel.getChannelid()); + } else { + map.put("deviceId", map.get("internationalId").toString()); + map.put("channelId", map.get("internationalId").toString()); + } + }); + if (ObjectUtil.isEmpty(r.get("channelList"))) { + r.put("channelList", substationPatroldevices); + } + } else { + patroldeviceCode = r.get("internationalId").toString(); + if (ObjUtil.isNotEmpty(r.get("channelinfo")) && JSONUtil.isTypeJSONArray(r.get("channelinfo").toString())) { + JSONArray channels = JSONUtil.parseArray(r.get("channelinfo").toString()); + List> mapList = new ArrayList<>(); + for (int i = 0; i < channels.size(); i++) { + JSONObject channel = channels.getJSONObject(i); + Map map = new HashMap<>(); + map.put("channelType", channel.getStr("channel_type")); + // 1:摄像机 + if ("1".equals(channel.getStr("from_device"))) { + map.put("deviceId", patroldeviceCode); + map.put("channelId", channel.getStr("channel_code")); + } else { + // 2:来源录像机 + map.put("deviceId", channel.getStr("recorder_code")); + map.put("channelId", channel.getStr("recorder_channelcode")); + } + mapList.add(map); + } + r.put("channelList", mapList); + } + } + }); + + mapPage.setRecords(records); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getPatrolDeviceResume") + @ApiOperation("根据机器人编号查询设备履历") + public ResponseResult getPatrolDeviceResume(String patrolDeviceCode, String type) { + if (StrUtil.isBlank(patrolDeviceCode)) { + return ResponseResult.error("设备编码为空"); + } + if (StrUtil.isBlank(type)) { + return ResponseResult.error("记录类型为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PatroldeviceResume::getPatroldeviceCode, patrolDeviceCode).eq(PatroldeviceResume::getType, + type); + List list = patroldeviceResumeService.list(queryWrapper); + return ResponseResult.successData(list); + } + + @Log(module = "巡视设备", value = "新增巡视设备",type = "1") + @PostMapping("/addPatrolDevice") + @ApiOperation("新增巡视设备") + @PreAuthorize("@el.check('add:patroldevice')") + public ResponseResult addPatrolDevice(@RequestBody SubstationPatroldevice substationPatroldevice) { + String patroldeviceCode = substationPatroldevice.getPatroldeviceCode(); + String uuid = IdUtil.fastSimpleUUID(); + substationPatroldevice.setPatroldeviceId(uuid); + String stationId = substationPatroldevice.getStationId(); + int count = + substationPatroldeviceService.count(new LambdaQueryWrapper().eq(SubstationPatroldevice::getStationId, stationId).eq(SubstationPatroldevice::getPatroldeviceCode, patroldeviceCode)); + if (count > 0) { + return ResponseResult.error("巡视设备已经存在"); + } + if ("10".equals(substationPatroldevice.getType()) || "11".equals(substationPatroldevice.getType())) { + SysUser userInfo = userService.getUserInfo(); + List roleId = userService.getRoleId(userInfo.getId()); + // 用户角色加上摄像头权限 + for (String id : roleId) { + log.info("roid--------------------" + id); + RolePatroldevice rolePatroldevice = new RolePatroldevice(); + rolePatroldevice.setDeviceid(uuid); + rolePatroldevice.setRoleid(id); + log.info(rolePatroldevice.toString()); + rolePatroldeviceService.save(rolePatroldevice); + } + } + + substationPatroldevice.setPatroldeviceId(uuid); + substationPatroldevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationPatroldevice.setLastmodifydate(LocalDateTime.now()); + substationPatroldevice.setDatastatus("1"); + boolean ok = substationPatroldeviceService.save(substationPatroldevice); + if (ok) { + return ResponseResult.successData(uuid); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "巡视设备", value = "新增或修改机器人", type = "1") + @PostMapping("/addOrUpdatePatrolRobot") + @ApiOperation("新增或修改机器人") + public ResponseResult addOrUpdatePatrolRobot(String substationPatroldevices, MultipartFile image) { + if (substationPatroldevices == null) { + return ResponseResult.error("参数为空"); + } + List substationPatroldeviceList = JSONUtil.toList(substationPatroldevices, + SubstationPatroldevice.class); + SubstationPatroldevice substationPatroldevice = substationPatroldeviceList.get(0); + String stationId = substationPatroldevice.getStationId(); + Set collect = + substationPatroldeviceList.stream().map(SubstationPatroldevice::getPatroldeviceCode).collect(Collectors.toSet()); + if (collect.size() != substationPatroldeviceList.size()) { + return ResponseResult.error("机器人编码与摄像头编码相同"); + } + for (SubstationPatroldevice substationPatroldevice1 : substationPatroldeviceList) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId).eq(SubstationPatroldevice::getPatroldeviceCode, substationPatroldevice1.getPatroldeviceCode()); + String patroldeviceId = substationPatroldevice1.getPatroldeviceId(); + if (StrUtil.isNotBlank(patroldeviceId)) { + queryWrapper.ne(SubstationPatroldevice::getPatroldeviceId, patroldeviceId); + } + int count = + substationPatroldeviceService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前设备已经存在"); + } + } + String fileName = ""; + List codeList = Arrays.asList("1", "2", "3", "4", "13"); + if (image != null && !image.isEmpty()) { + String filename = image.getOriginalFilename(); + String suffix = StrUtil.subAfter(filename, ".", true).toLowerCase(); + if (!"png".equals(suffix) && !"jpg".equals(suffix) && !"gif".equals(suffix)) { + return ResponseResult.error("非法文件类型"); + } + String contentType = image.getContentType(); + if (!"image/jpeg".equals(contentType) && !"image/png".equals(contentType) && !"image/gif".equals(contentType)) { + return ResponseResult.error("非法文件内容"); + } + // 图片存储地址 + String imgPath = systempath + "patrolDevice"; + String iconname = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(image.getOriginalFilename()); + //获取图片名称 + fileName = Objects.requireNonNull(FileUtil.upload(image, imgPath, iconname)).getName(); + } + String finalFileName = fileName; + substationPatroldeviceList.forEach(r -> { + if (codeList.contains(r.getType())&&StrUtil.isNotBlank(finalFileName)) { + r.setCustom2(finalFileName); + } + r.setOnline("1"); + }); + substationPatroldeviceService.addOrUpdatePatrolRobot(substationPatroldeviceList); + return ResponseResult.success(); + } + + @Log(module = "巡视设备", value = "修改巡视设备",type = "1") + @PostMapping("/updatePatrolDeviceById") + @ApiOperation("修改巡视设备") + @PreAuthorize("@el.check('update:patroldevice')") + public ResponseResult updateSubstationDeviceById(@RequestBody SubstationPatroldevice substationPatroldevice) { + String stationId = substationPatroldevice.getStationId(); + String patroldeviceCode = substationPatroldevice.getPatroldeviceCode(); + String patroldeviceId = substationPatroldevice.getPatroldeviceId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getPatroldeviceCode, patroldeviceCode).eq(SubstationPatroldevice::getStationId, stationId).ne(SubstationPatroldevice::getPatroldeviceId, patroldeviceId); + int count = substationPatroldeviceService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前巡视设备已经存在"); + } + substationPatroldevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationPatroldevice.setLastmodifydate(LocalDateTime.now()); + boolean ok = substationPatroldeviceService.updateById(substationPatroldevice); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @GetMapping("/getSubstationDeviceById") + @ApiOperation("根据Id查询巡视设备完整率") + public ResponseResult getSubstationDeviceById(String id, String internationalId, String channelid) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(id)) { + queryWrapper.eq(SubstationPatroldevice::getPatroldeviceId, id); + } + if (StrUtil.isNotBlank(internationalId)) { + queryWrapper.eq(SubstationPatroldevice::getInternationalId, internationalId); + } + List list = substationPatroldeviceService.list(queryWrapper); + if (list.size() <= 0) { + return ResponseResult.successData(null); + } + SubstationPatroldevice substationPatroldevice = list.get(0); + if (StrUtil.isBlank(channelid)) { + return ResponseResult.successData(substationPatroldevice); + } + Map map = BeanUtil.beanToMap(substationPatroldevice); + DeviceChannel deviceChannel = + substationPatroldeviceService.queryChannelByDeviceIp(substationPatroldevice.getIpaddr()); + if (deviceChannel == null) { + map.put("duration", 0); + map.put("rate", 0); + } else { + String cameraStatByCode = substationPatroldeviceService.getCameraStatByCode(deviceChannel.getChannelid()); + if (StrUtil.isBlank(cameraStatByCode)) { + map.put("duration", 0); + map.put("rate", 0); + } else { + long duration = Long.parseLong(cameraStatByCode) / 60; + map.put("duration", duration); + long sum = 90 * 24 * 60; + map.put("rate", (int) (((float) duration / sum) * 100 + 0.5)); + } + } + map.put("cycle", 90); + return ResponseResult.successData(map); + } + + @GetMapping("/getPatroldeviceById") + @ApiOperation("根据Id查询巡视设备") + public ResponseResult getSubstationDeviceById(String id) { + SubstationPatroldevice substationPatroldevice = substationPatroldeviceService.getById(id); + return ResponseResult.successData(substationPatroldevice); + } + + @Log(module = "巡视设备", value = "删除巡视设备", type = "1") + @PostMapping("/deletePatrolDeviceById") + @ApiOperation("删除巡视设备") + @PreAuthorize("@el.check('del:patroldevice')") + public ResponseResult deleteSubstationDeviceById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = substationPatroldeviceService.deleteSubstationDeviceById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "巡视设备", value = "设置巡视设备状态是否启用",type = "1") + @PostMapping("/setPatrolDeviceStatus") + @ApiOperation("设置巡视设备状态是否启用") + public ResponseResult setSubstationDeviceStatus(String id, String dataStatus) { + boolean ok = substationPatroldeviceService.setSubstationDeviceStatus(id, dataStatus); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @GetMapping("/getSilentConfiguration") + @ApiOperation("获取静默配置") + public ResponseResult getSilentConfiguration(String code) { + if (StrUtil.isBlank(code)) { + return ResponseResult.error("参数为空"); + } + return ResponseResult.success(); + } + + @GetMapping("/getCameraByArea") + @ApiOperation("获取变电站下的摄像头(不包含机器人)") + public ResponseResult getCameraByArea(String stationId, String deviceName, String status) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("未传变电站信息"); + } + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + if (usertype != 0) { + //if (usertype != 0 && !"14".equals(type)) { + // 非管理员只可以查看指定摄像头 + List ids = substationPatroldeviceService.getCameraIds(userInfo.getId()); + // 获取用户所有的摄像头 + if (ids.size() > 0) { + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, ids); + } else { + return ResponseResult.successData(null); + } + } + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, deviceName); + } + if (StrUtil.isNotBlank(status)) { + queryWrapper.eq(SubstationPatroldevice::getOnline, status); + } + queryWrapper.and(i -> i.isNull(SubstationPatroldevice::getRobotsCode).or().eq(SubstationPatroldevice::getRobotsCode, "")); + queryWrapper.eq(SubstationPatroldevice::getType, "10").eq(SubstationPatroldevice::getDatastatus, "1").isNotNull(SubstationPatroldevice::getAreaName); + List> maps = substationPatroldeviceService.listMaps(queryWrapper); + Map>> deviceList = maps.stream().filter(m -> StrUtil.isNotBlank(m.get( + "areaName").toString())).collect(Collectors.groupingBy(m -> m.get("areaName"))); + // 将摄像头转成树 + List> deviceTree = new ArrayList<>(); + for (Object o : deviceList.keySet()) { + Map map = new HashMap<>(); + List> maps1 = deviceList.get(o); + List> maps2 = new ArrayList<>(); + for (Map m : maps1) { + if (ObjUtil.isNotEmpty(m.get("channelinfo")) && JSONUtil.isTypeJSONArray(m.get("channelinfo").toString())) { + JSONArray channels = JSONUtil.parseArray(m.get("channelinfo").toString()); + for (int i = 0; i < channels.size(); i++) { + JSONObject channel = channels.getJSONObject(i); + Map copyMap = new HashMap<>(m); + copyMap.put("channelType", channel.getStr("channel_type")); + if ("2".equals(channel.getStr("channel_type"))) { + copyMap.put("patroldeviceName", m.get("patroldeviceName").toString() + "_红外"); + } + // 1:摄像机 + if ("1".equals(channel.getStr("from_device"))) { + copyMap.put("deviceId", m.get("internationalId").toString()); + copyMap.put("channelId", channel.getStr("channel_code")); + } else { + // 2:来源录像机 + copyMap.put("deviceId", channel.getStr("recorder_code")); + copyMap.put("channelId", channel.getStr("recorder_channelcode")); + } + copyMap.put("channelRtspurl", channel.getStr("channel_rtspurl"));//如果指定了视频流地址,则 + maps2.add(copyMap); + } + } + else{//没有配置 + String patroldeviceCode = m.get("internationalId").toString(); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(DeviceChannel::getDeviceid, patroldeviceCode).eq(DeviceChannel::getAddress, "Address") + .select(DeviceChannel::getDeviceid, + DeviceChannel::getChannelid) + .orderByAsc(DeviceChannel::getChannelid); + List> channels = deviceChannelService.listMaps(queryWrapper1); + for (int i = 0; i < channels.size(); i++) { + Map channel = channels.get(i); + Map copyMap = new HashMap<>(map); + copyMap.put("patroldeviceName", map.get("patroldeviceName").toString() + "_" + (i + 1)); + copyMap.put("deviceId", patroldeviceCode); + copyMap.put("channelId", channel.get("channelId").toString()); + maps2.add(copyMap); + } + } + } + map.put("name", o); + map.put("children", maps2); + deviceTree.add(map); + } + return ResponseResult.successData(deviceTree); + } + + @GetMapping("/getPatrolDeviceOnLine") + @ApiOperation("获取巡视设备在线率(单站)") + public ResponseResult getPatrolDeviceOnLine(String stationId) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("未传变电站信息"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + if (usertype != 0) { + // 非管理员只可以查看指定摄像头 + List ids = substationPatroldeviceService.getCameraIds(userInfo.getId()); + // 获取用户所有的摄像头 + if (ids.size() > 0) { + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, ids); + } else { + return ResponseResult.successData(null); + } + } + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId).eq(SubstationPatroldevice::getDatastatus, "1"); + List list = substationPatroldeviceService.list(queryWrapper); + String[] codeArray = {"1", "2", "3", "4", "10", "13"}; + List codeList = Arrays.asList(codeArray); + Map> collect = + list.stream().filter(l -> (StrUtil.isNotBlank(l.getType()) && codeList.contains(l.getType()))).collect(Collectors.groupingBy(SubstationPatroldevice::getType)); + Map map = new HashMap<>(); + for (String code : codeList) { + List substationPatroldevices = collect.get(code); + Map map1 = new HashMap<>(); + if (substationPatroldevices == null) { + map1.put("allCount", 0); + map1.put("online", 0); + map1.put("onLineRate", 0); + map.put(code, map1); + continue; + } + List collect1 = + substationPatroldevices.stream().filter(s -> "1".equals(s.getOnline())).collect(Collectors.toList + ()); + int allCount1 = substationPatroldevices.size(); + int online1 = collect1.size(); + + map1.put("allCount", allCount1); + map1.put("online", online1); + map1.put("onLineRate", (int) (((float) online1 / allCount1) * 100 + 0.5)); + map.put(code, map1); + } + // 机器人 + Map map1 = BeanUtil.beanToMap(map.get("1")); + map.remove("1"); + int allCount1 = (Integer) map1.get("allCount"); + int online1 = (Integer) map1.get("online"); + Map map2 = BeanUtil.beanToMap(map.get("2")); + map.remove("2"); + int allCount2 = (Integer) map2.get("allCount"); + int online2 = (Integer) map2.get("online"); + Map map3 = BeanUtil.beanToMap(map.get("3")); + map.remove("3"); + int allCount3 = (Integer) map3.get("allCount"); + int online3 = (Integer) map3.get("online"); + Map map4 = BeanUtil.beanToMap(map.get("4")); + map.remove("4"); + int allCount4 = (Integer) map4.get("allCount"); + int online4 = (Integer) map4.get("online"); + int count = allCount1 + allCount2 + allCount3 + allCount4; + int online = online1 + online2 + online3 + online4; + Map mapAll = new HashMap<>(); + mapAll.put("allCount", count); + mapAll.put("online", online); + mapAll.put("onLineRate", (int) (((float) online / count) * 100 + 0.5)); + map.put("robot", mapAll); + // 无人机 + Map uav = BeanUtil.beanToMap(map.get("13")); + map.remove("13"); + map.put("uav", uav); + // 摄像头 + Map camera = BeanUtil.beanToMap(map.get("10")); + map.remove("10"); + map.put("camera", camera); + return ResponseResult.successData(map); + } + + @GetMapping("/getPatrolDeviceStat") + @ApiOperation("巡视设备统计(多站)") + public ResponseResult getPatrolDeviceStat(String stationId) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("未传变电站信息"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + if (usertype != 0) { + // 非管理员只可以查看指定摄像头 + List ids = substationPatroldeviceService.getCameraIds(userInfo.getId()); + // 获取用户所有的摄像头 + if (ids.size() > 0) { + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, ids); + } else { + return ResponseResult.successData(null); + } + } + // 判断多个变电站的设备在线率 + List idList = Arrays.asList(stationId.split(",")); + queryWrapper.in(SubstationPatroldevice::getStationId, idList).eq(SubstationPatroldevice::getDatastatus, "1"); + List list = substationPatroldeviceService.list(queryWrapper); + String[] codeArray = {"1", "2", "3", "10", "11", "12", "13", "14", "15", "21"}; + List codeList = Arrays.asList(codeArray); + Map> collect = + list.stream().filter(l -> (StrUtil.isNotBlank(l.getType()) && codeList.contains(l.getType()))).collect(Collectors.groupingBy(SubstationPatroldevice::getType)); + Map map = new HashMap<>(); + for (String code : codeList) { + List substationPatroldevices = collect.get(code); + Map map1 = new HashMap<>(); + if (substationPatroldevices == null) { + map1.put("allCount", 0); + map1.put("online", 0); + map1.put("onLineRate", 0); + Map nameByCode = getNameByCode(code); + if (nameByCode != null) { + map1.put("name", nameByCode.get("nameCn")); + map.put(nameByCode.get("nameEn"), map1); + } + continue; + } + List collect1 = + substationPatroldevices.stream().filter(s -> "1".equals(s.getOnline())).collect(Collectors.toList + ()); + int allCount1 = substationPatroldevices.size(); + int online1 = collect1.size(); + map1.put("allCount", allCount1); + map1.put("online", online1); + map1.put("onLineRate", (int) (((float) online1 / allCount1) * 100 + 0.5)); + Map nameByCode = getNameByCode(code); + if (nameByCode != null) { + map1.put("name", nameByCode.get("nameCn")); + map.put(nameByCode.get("nameEn"), map1); + } + + } + // 轮式机器人 + Map map1 = BeanUtil.beanToMap(map.get("outWheelRobot")); + int allCount1 = (Integer) map1.get("allCount"); + int online1 = (Integer) map1.get("online"); + Map map2 = BeanUtil.beanToMap(map.get("inWheelRobot")); + int allCount2 = (Integer) map2.get("allCount"); + int online2 = (Integer) map2.get("online"); + Map wheelMap = new HashMap<>(); + int count = allCount1 + allCount2; + int online = online1 + online2; + wheelMap.put("allCount", count); + wheelMap.put("online", online); + wheelMap.put("onLineRate", (int) (((float) online / count) * 100 + 0.5)); + wheelMap.put("name", "轮式机器人"); + map.put("wheelRobot", wheelMap); + return ResponseResult.successData(map); + } + + /********************************** + * 用途说明: 根据字典编码获取Map集合Key值 + * 参数说明 code + * 返回值说明: java.lang.String + ***********************************/ + private Map getNameByCode(String code) { + Map map = new HashMap<>(); + if ("1".equals(code)) { + map.put("nameEn", "outWheelRobot"); + map.put("nameCn", "室外轮式机器人"); + return map; + } else if ("2".equals(code)) { + map.put("nameEn", "inWheelRobot"); + map.put("nameCn", "室内轮式机器人"); + return map; + } else if ("3".equals(code)) { + map.put("nameEn", "hangTrackRobot"); + map.put("nameCn", "挂轨机器人"); + return map; + } else if ("10".equals(code)) { + map.put("nameEn", "camera"); + map.put("nameCn", "摄像机"); + return map; + } else if ("11".equals(code)) { + map.put("nameEn", "dvr"); + map.put("nameCn", "硬盘录像机"); + return map; + } else if ("12".equals(code)) { + map.put("nameEn", "analysisHost"); + map.put("nameCn", "智能分析主机"); + return map; + } else if ("13".equals(code)) { + map.put("nameEn", "uav"); + map.put("nameCn", "无人机"); + return map; + } else if ("14".equals(code)) { + map.put("nameEn", "aVoicePrint"); + map.put("nameCn", "声纹"); + return map; + } else if ("15".equals(code)) { + map.put("nameEn", "uavNest"); + map.put("nameCn", "无人机机巢"); + return map; + } else if ("20".equals(code)) { + map.put("nameEn", "areaPatrolHost"); + map.put("nameCn", "区域巡视主机"); + return map; + } + return null; + } + + @GetMapping("/isWorkingOfPatrolDevice") + @ApiOperation("摄像头是否正在执行巡视任务") + public ResponseResult isWorkingOfPatrolDevice(String stationcode, String devicecode) { + String isWorking = substationPatroldeviceService.isWorkingofPatroldevice(stationcode, devicecode); + return ResponseResult.successData(isWorking); + } + + @GetMapping("/getPatrolDeviceStatus") + @ApiOperation("获取摄像头状态") + public ResponseResult getPatrolDeviceStatus(String patrolDeviceId) { + + if (StrUtil.isBlank(patrolDeviceId)) { + return ResponseResult.success(); + } + List idList = Arrays.asList(patrolDeviceId.split(",")); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SubstationPatroldevice::getPatroldeviceId, idList).select(SubstationPatroldevice::getPatroldeviceId, SubstationPatroldevice::getOnline); + List> mapList = substationPatroldeviceService.listMaps(queryWrapper); + return ResponseResult.successData(mapList); + } + + @Log(module = "巡视设备", value = "设置静默监视是否启用",type = "1") + @PostMapping("/setDailyMonitorStatus") + @ApiOperation("设置静默监视是否启用") + public ResponseResult setDailyMonitorStatus(String id, String dailyMonitor) { + boolean ok = substationPatroldeviceService.setDailyMonitorStatus(id, dailyMonitor); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @GetMapping("/getCameraIntegrityRate") + @ApiOperation("摄像机录像完整率统计") + public ResponseResult getCameraIntegrityRate(Page> page, String stationId, + String patrolDeviceName, String channel, String flag) { + + Page> mapPage = substationPatroldeviceService.getCameraStat(page, stationId, + patrolDeviceName, channel, flag); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getCameraStatByCode") + @ApiOperation("单个摄像机完整率") + public ResponseResult getCameraStatByCode(String channelId) { + String cameraStatByCode = substationPatroldeviceService.getCameraStatByCode(channelId); + Map map = new HashMap<>(); + long sum = 90 * 24 * 60; + long duration = Long.parseLong(cameraStatByCode) / 60; + map.put("rate", (int) (((float) duration / sum) * 100 + 0.5)); + map.put("duration", duration); + map.put("cycle", 90); + return ResponseResult.successData(map); + } + + @GetMapping("/getRobotCamera") + @ApiOperation("获取机器人的摄像头") + public ResponseResult getRobotCamera(String patrolDeviceCode) { + SubstationPatroldevice substationPatroldevice = + substationPatroldeviceService.getOne(new LambdaQueryWrapper().eq(SubstationPatroldevice::getRobotsCode, patrolDeviceCode).eq(SubstationPatroldevice::getDatastatus, "1")); + return ResponseResult.successData(substationPatroldevice); + } + + @GetMapping("/getRobotOnlineRate") + @ApiOperation("巡视设备累计运行天数") + public ResponseResult getRobotOnlineRate(Page> page, String stationId, + String patrolDeviceName, String patrolDeviceCode, + String type) { + // + // Page> mapPage = substationPatroldeviceService.getRobotOnlineRate(page, stationId, + // patrolDeviceName, patrolDeviceCode, type); + Page> onlineDurationPage = robotOfflineLogService.getOnlineDurationPage(page, stationId, + patrolDeviceCode, patrolDeviceName, type); + return ResponseResult.successData(onlineDurationPage); + } + + @GetMapping("/getRobotAndUavList") + @ApiOperation("获取机器人/无人机") + public ResponseResult getRobotList() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SubstationPatroldevice::getType, "1", "2", "3", "4", "13", "15"); + queryWrapper.select(SubstationPatroldevice::getPatroldeviceId,SubstationPatroldevice::getType, SubstationPatroldevice::getPatroldeviceCode, + SubstationPatroldevice::getPatroldeviceName); + List> mapList = substationPatroldeviceService.listMaps(queryWrapper); + return ResponseResult.successData(mapList); + } + + @PostMapping("/setEffectiveArea") + @ApiOperation("设置静默监视有效区域") + public ResponseResult setEffectiveArea(String type, String id, String effecttivearea) { + substationPatroldeviceService.setEffectiveArea(type, id, effecttivearea); + return ResponseResult.success(); + } + + @GetMapping("/exportPatrolDeviceList") + @ApiOperation("导出巡视设备台账") + public void exportPatrolDeviceList(String stationId, String type, String patrolDeviceName, String deviceModel, + String manufacturer, HttpServletResponse response) throws IOException { + + substationPatroldeviceService.exportPatrolDeviceList(stationId, type, patrolDeviceName, deviceModel, + manufacturer, response); + } + + @GetMapping("/getDvrStorageStatus") + @ApiOperation("获取硬盘录像机存储数据") + public ResponseResult getDvrStorageStatus(String rtspUrl) { + if (StrUtil.isNotBlank(rtspUrl)) { + String reg = "//([^/]+)"; + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(rtspUrl); + if (!matcher.find()) { + throw new RuntimeException("rtsp地址错误"); + } + String group = matcher.group(1); + String replace = group.replace("@", ":"); + String[] split = replace.split(":"); + String username = split[0]; + String password = split[1]; + String ip = split[2]; + log.info("-------调用硬盘录像机存储方法--------"); + log.info("DLL_PATH" + CameraUtil.DLL_PATH); + String nvrStatus = CameraUtil.getStorageStatus("haikang", ip, (short) 8000, username, password); + JSONObject jsonObject = JSONUtil.parseObj(nvrStatus); + // 硬盘容量 + String dwCapacity = jsonObject.getStr("dwCapacity"); + int capacity = NumberUtil.parseInt(dwCapacity); + jsonObject.putOpt("dwCapacity", String.format("%.2f", (float) capacity / 1024)); + // 硬盘剩余空间 + String dwFreeSpace = jsonObject.getStr("dwFreeSpace"); + int freeSpace = NumberUtil.parseInt(dwFreeSpace); + jsonObject.putOpt("dwFreeSpace", String.format("%.2f", (float) freeSpace / 1024)); + // 已用容量 + int usedSpace = capacity - freeSpace; + jsonObject.putOnce("usedSpace", String.format("%.2f", (float) usedSpace / 1024)); + String rate = String.format("%.2f", ((float) usedSpace / (float) capacity) * 100); + jsonObject.putOnce("rate", rate); + return ResponseResult.successData(jsonObject); + } + return ResponseResult.successData(null); + } + + @GetMapping("/login") + @ApiOperation("登录黑卡摄像机登录") + public ResponseResult login() { + List heikaList = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getManufacturer, "heika")); + if (heikaList.size() > 0) { + JSONArray jsonArray = new JSONArray(); + for (SubstationPatroldevice substationPatroldevice : heikaList) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("ip", substationPatroldevice.getIpaddr()); + String reg = "//([^/]+)"; + Pattern pattern = Pattern.compile(reg); + String channelinfo = substationPatroldevice.getChannelinfo(); + JSONArray jsonArray1 = JSONUtil.parseArray(channelinfo); + String rtspurl = ""; + if (jsonArray1.size() > 0) { + JSONObject jsonObject1 = jsonArray1.getJSONObject(0); + rtspurl = jsonObject1.getStr("channel_rtspurl"); + } + Matcher matcher = pattern.matcher(rtspurl); + if (!matcher.find()) { + continue; + } + String group = matcher.group(1); + int lastAtIndex = group.lastIndexOf('@'); + // 如果找到了@ + if (lastAtIndex != -1) { + // 检查@后面是否紧跟着IP地址和端口号,但在这个例子中我们假设没有 + // 所以直接替换最后一个@为: + group = group.substring(0, lastAtIndex) + ":" + group.substring(lastAtIndex + 1); + } else { + log.error("rtsp地址错误"); + } + String[] split = group.split(":"); + String username = split[0]; + String password = split[1]; + jsonObject.putOpt("username", username); + jsonObject.putOpt("password", password); + jsonArray.add(jsonObject); + + } + if (jsonArray.size() > 0) { + Map param = new HashMap<>(); + param.put("list", jsonArray.toString()); + String api = "login"; + if (StrUtil.isNotBlank(config.getHeikaApp())) { + api = config.getHeikaApp() + "/login"; + } + com.alibaba.fastjson.JSONObject result = new com.alibaba.fastjson.JSONObject(); + httpUtil.sendHttpPost("json", config.getSdkIp(), + config.getHeikaPort(), "", api, param, null); + } + } + return ResponseResult.success(); + } + + @GetMapping("/logout") + @ApiOperation("注销黑卡摄像机登录") + public ResponseResult logout() { + List heikaList = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getManufacturer, "heika")); + if (heikaList.size() > 0) { + JSONArray jsonArray = new JSONArray(); + for (SubstationPatroldevice substationPatroldevice : heikaList) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOpt("ip", substationPatroldevice.getIpaddr()); + String reg = "//([^/]+)"; + Pattern pattern = Pattern.compile(reg); + String channelinfo = substationPatroldevice.getChannelinfo(); + JSONArray jsonArray1 = JSONUtil.parseArray(channelinfo); + String rtspurl = ""; + if (jsonArray1.size() > 0) { + JSONObject jsonObject1 = jsonArray1.getJSONObject(0); + rtspurl = jsonObject1.getStr("channel_rtspurl"); + } + Matcher matcher = pattern.matcher(rtspurl); + if (!matcher.find()) { + continue; + } + String group = matcher.group(1); + int lastAtIndex = group.lastIndexOf('@'); + // 如果找到了@ + if (lastAtIndex != -1) { + // 检查@后面是否紧跟着IP地址和端口号,但在这个例子中我们假设没有 + // 所以直接替换最后一个@为: + group = group.substring(0, lastAtIndex) + ":" + group.substring(lastAtIndex + 1); + } else { + log.error("rtsp地址错误"); + } + String[] split = group.split(":"); + String username = split[0]; + String password = split[1]; + jsonObject.putOpt("username", username); + jsonObject.putOpt("password", password); + jsonArray.add(jsonObject); + + } + if (jsonArray.size() > 0) { + Map param = new HashMap<>(); + param.put("list", jsonArray.toString()); + String api = "logout"; + if (StrUtil.isNotBlank(config.getHeikaApp())) { + api = config.getHeikaApp() + "/logout"; + } + com.alibaba.fastjson.JSONObject result = new com.alibaba.fastjson.JSONObject(); + httpUtil.sendHttpPost("json", config.getSdkIp(), + config.getHeikaPort(), "", api, param, null); + } + } + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/WeatherLogController.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/WeatherLogController.java new file mode 100644 index 0000000..51975fb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/controller/WeatherLogController.java @@ -0,0 +1,42 @@ +package com.yfd.platform.modules.basedata.controller; + +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.service.IWeatherLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.Map; + +/** + *

+ * 微气象数据日志表 前端控制器 + *

+ * + * + * @since 2023-04-27 + */ +@RestController +@RequestMapping("/basedata/weather-log") +@Api(value = "WeatherLogController", tags = "微气象数据日志") +public class WeatherLogController { + + @Resource + private IWeatherLogService weatherLogService; + + @GetMapping("/getWeatherLogList") + @ApiOperation("查询环境信息") + public ResponseResult getWeatherLogList(String stationId) { + + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("未传变电站信息"); + } + Map weatherDevice = weatherLogService.getWeatherLogList(stationId); + return ResponseResult.successData(weatherDevice); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/AlgorithmManufacturerVersion.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/AlgorithmManufacturerVersion.java new file mode 100644 index 0000000..7fea7ab --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/AlgorithmManufacturerVersion.java @@ -0,0 +1,53 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * + *

+ * + * @author zhengsl + * @since 2024-05-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_algorithm_manufacturer_version") +public class AlgorithmManufacturerVersion implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 算法厂商 + */ + private String algorithmManufacturer; + + /** + * 版本ID + */ + private String version; + + /** + * 该版本记录时间 + */ + private String recordTime; + + /** + * 模型应用场景、评价指标等描 述 + */ + private String modelDesc; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/AlgorithmModel.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/AlgorithmModel.java new file mode 100644 index 0000000..f0d7b8b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/AlgorithmModel.java @@ -0,0 +1,140 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 算法模型表 + *

+ * + * + * @since 2023-08-25 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_algorithm_model") +public class AlgorithmModel implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 区域ID + */ + private String sectionId; + + /** + * 区域名称 + */ + private String sectionName; + + /** + * 变电站ID + */ + private String stationId; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 模型编号 + */ + private String modelCode; + + /** + * 模型名称 + */ + private String modelName; + + /** + * 模型文件包 + */ + private String modelFile; + + /** + * 1:图像识别;2:图像判别 3:一键顺控视频分析 + */ + private String modelType; + + /** + * 算法描述 + */ + private String algorithmdesc; + + /** + * 算法厂商 + */ + private String algorithmmanufacturer; + + /** + * 算法版本 + */ + private String version; + + /** + * 0-否 1-新 是否最新版本 + */ + private String isnew; + + /** + * 中间版本数 + */ + private Integer midversionnum; + + /** + * 模型运行状态,1:运行 2:停止 + */ + private String runstate; + + /** + * 上传/更新人 + */ + private String lastmodifier; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime recordtime; + + /** + * 最近更新时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + /** + * 算法最新版本 + */ + private String lastVersion; + + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/DeviceChannel.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/DeviceChannel.java new file mode 100644 index 0000000..a5f97fd --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/DeviceChannel.java @@ -0,0 +1,237 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 设备下的通道信息(每个设备查看视频的主要信息表) + + *

+ * + * + * @since 2023-04-21 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("mon_device_channel") +public class DeviceChannel implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 数据库自增ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + /** + * 通道国标编号 + */ + @TableField("channelId") + private String channelid; + + /** + * 设备国标编号 + */ + @TableField("deviceId") + private String deviceid; + + /** + * 通道名称 + */ + private String name; + + /** + * 生产厂商 + */ + private String manufacture; + + /** + * 号型 + */ + private String model; + + /** + * 设备归属 + */ + private String owner; + + /** + * 行政区域 + */ + @TableField("civilCode") + private String civilcode; + + /** + * 警区 + */ + private String block; + + /** + * 安装地址 + */ + private String address; + + /** + * 是否有子设备 1有, 0没有 + */ + private String parental; + + /** + * 父级id + */ + @TableField("parentId") + private String parentid; + + /** + * 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式 + */ + @TableField("safetyWay") + private Integer safetyway; + + /** + * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式 + */ + @TableField("registerWay") + private Integer registerway; + + /** + * 证书序列号 + */ + @TableField("certNum") + private String certnum; + + /** + * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 + */ + private Integer certifiable; + + /** + * 证书无效原因码 + */ + @TableField("errCode") + private Integer errcode; + + /** + * 证书终止有效期 + */ + @TableField("endTime") + private String endtime; + + /** + * 保密属性 缺省为0; 0:不涉密, 1:涉密 + */ + private String secrecy; + + /** + * IP地址 + */ + @TableField("ipAddress") + private String ipaddress; + + /** + * 端口号 + */ + private Integer port; + + /** + * 密码 + */ + private String password; + + /** + * 云台类型 + */ + @TableField("PTZType") + private Integer ptztype; + + /** + * 创建时间 + */ + @TableField("createTime") + private String createtime; + + /** + * 更新时间 + */ + @TableField("updateTime") + private String updatetime; + + /** + * 在线/离线, 1在线,0离线 + */ + private Integer status; + + /** + * 经度 + */ + private Double longitude; + + /** + * 纬度 + */ + private Double latitude; + + /** + * GCJ02坐标系经度 + */ + @TableField("longitudeGcj02") + private Double longitudegcj02; + + /** + * GCJ02坐标系纬度 + */ + @TableField("latitudeGcj02") + private Double latitudegcj02; + + /** + * WGS84坐标系经度 + */ + @TableField("longitudeWgs84") + private Double longitudewgs84; + + /** + * WGS84坐标系纬度 + */ + @TableField("latitudeWgs84") + private Double latitudewgs84; + + /** + * 子设备数 + */ + @TableField("subCount") + private Integer subcount; + + /** + * 流唯一编号,存在表示正在直播 + */ + @TableField("streamId") + private String streamid; + + /** + * 是否含有音频 + */ + @TableField("hasAudio") + private Boolean hasaudio; + + /** + * 业务分组 + */ + @TableField("businessGroupId") + private String businessgroupid; + + /** + * GPS更新时间 + */ + @TableField("gpsTime") + private String gpstime; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/LinkageSignal.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/LinkageSignal.java new file mode 100644 index 0000000..69a88af --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/LinkageSignal.java @@ -0,0 +1,146 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 联动信号关联表 + *

+ * + * + * @since 2023-04-06 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_linkage_signal") +public class LinkageSignal implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 序号 + */ + private String orderNum; + + /** + * 站序号,当前实际站号编制 + */ + private String stationNum; + + /** + * 监控索引号为监控系统测点 ID,可用于数据的唯一标识 + */ + private String controlNum; + + /** + * 称用于详细描述设备的信息 + */ + private String linkageDeviceName; + + /** + * 包括遥信、遥测 + */ + private String linkageDeviceType; + + /** + * 实物ID + */ + private String materialId; + + /** + * 是否联动信号,是/否 + */ + private String isLinkageFlag; + + /** + * 变电站id + */ + private String stationId; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 变电站编码 + */ + private String stationCode; + + /** + * 时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime time; + + /** + * 巡视点位id列表,','分隔 + */ + private String deviceIdList; + + /** + * 关联视频编码及预置位,json格式 + */ + private String videoPos; + + /** + * 联动类型(一件顺控、智能联动) + */ + private String linkageType; + + /** + * 数据状态,0:未绑定,1:已绑定,默认0 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 是否有效;0:无效;1:有效 + */ + private String isenable; + + /** + * 机器人编码 + */ + private String robotCode; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/PatroldeviceResume.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/PatroldeviceResume.java new file mode 100644 index 0000000..c92c95a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/PatroldeviceResume.java @@ -0,0 +1,106 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 机器人及无人机履历表 + *

+ * + * + * @since 2023-04-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_patroldevice_resume") +public class PatroldeviceResume implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 变电站id + */ + private String stationId; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 变电站编码 + */ + private String stationCode; + + /** + * 巡视设备编号 + */ + private String patroldeviceCode; + + /** + * 巡视设备名称 + */ + private String patroldeviceName; + + /** + * 类型:1:缺陷记录;2:大修记录;3:退出再投运记录 + */ + private String type; + + /** + * 时间 + */ + private String time; + + /** + * 描述 + */ + private String comment; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/Substation.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/Substation.java new file mode 100644 index 0000000..18b5046 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/Substation.java @@ -0,0 +1,166 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation") +public class Substation implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "station_id", type = IdType.ASSIGN_UUID) + private String stationId; + + /** + * 变电站类别 + */ + private String stationType; + /** + * 变电站名称 + */ + private String stationName; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站IP + */ + private String stationIp; + + /** + * 变电站节点ID + */ + private String nodeId; + + /** + * 电压等级 + */ + private String voltLevel; + + /** + * 所属地市 + */ + private String cityName; + + /** + * 所属省份 + */ + private String provinceName; + + /** + * 公司名称 + */ + private String companyName; + + /** + * 区域ID + */ + private String sectionId; + + /** + * 区域名称 + */ + private String sectionName; + + /** + * 变电站地址 + */ + private String stationAddress; + + /** + * 联系信息 + */ + /*private String contactInfo;*/ + + /** + * P + * 联系电话 + */ + private String contactPhone; + + /** + * 联系人 + */ + private String contactPerson; + + /** + * 变电站介绍 + */ + private String introduction; + + /** + * 变电站图片 + */ + private String images; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 是否是边缘节点 0:否,1:是 + */ + private String isStationFlag; + + /** + * 坐标位置,经度,维度,逗号分割 + */ + private String coordinate; + + /** + * 边缘节点是否在线 1-在线 0-离线 + */ + private String online; + + /** + * 备用1(排序编号) + */ + private Integer custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationArea.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationArea.java new file mode 100644 index 0000000..4f1268d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationArea.java @@ -0,0 +1,81 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站_区域 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_area") +public class SubstationArea implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 区域id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String areaId; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 区域名称 + */ + private String areaName; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1(排序编号) + */ + private Integer custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationBay.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationBay.java new file mode 100644 index 0000000..b44d7b7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationBay.java @@ -0,0 +1,91 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站_间隔 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_bay") +public class SubstationBay implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 间隔id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String bayId; + + /** + * 间隔名称 + */ + private String bayName; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 区域id + */ + private String areaId; + + /** + * 区域名称 + */ + private String areaName; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1(排序编号) + */ + private Integer custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationComponent.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationComponent.java new file mode 100644 index 0000000..4294eb5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationComponent.java @@ -0,0 +1,122 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站_设备部件 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_component") +public class SubstationComponent implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 部件id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String componentId; + + /** + * 部件名称 + */ + private String componentName; + + /** + * 主设备id + */ + private String mainDeviceId; + + /** + * 主设备类型 + */ + private String deviceType; + + /** + * 主设备名称 + */ + private String mainDeviceName; + + /** + * 间隔id + */ + private String bayId; + + /** + * 间隔名称 + */ + private String bayName; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 区域id + */ + private String areaId; + + /** + * 区域名称 + */ + private String areaName; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1 + */ + private Integer custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 实物ID + */ + @TableField("material_id") + private String materialId; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationDevice.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationDevice.java new file mode 100644 index 0000000..9dcccb5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationDevice.java @@ -0,0 +1,286 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站_巡视点位 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_device") +public class SubstationDevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 设备点位id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String deviceId; + + /** + * 设备点位编号 + */ + private String deviceCode; + + /** + * 设备点位名称 + */ + private String deviceName; + + /** + * 变电站id + */ + private String stationId; + + private String stationName; + + /** + * 和中台对应资源 ID 保持一致 + */ + private String stationCode; + + /** + * 和中台对应资源 ID 保持一致 + */ + private String areaId; + + /** + * 区域全名 + */ + private String areaName; + + /** + * 和中台对应资源 ID 保持一致 + */ + private String bayId; + + /** + * 间隔全名 + */ + private String bayName; + + /** + * 主设备ID,和中台对应资源 ID 保持一致 + */ + private String mainDeviceId; + + /** + * 主设备全名 + */ + private String mainDeviceName; + + /** + * 主设备类型,字典项,选择主设备列表的时候带过来 + */ + private String deviceType; + + /** + * 和中台对应资源 ID 保持一致 + */ + private String componentId; + + /** + * 部件名称 + */ + private String componentName; + + /** + * 表计类型,字典项 + */ + private String meterType; + + /** + * 辅助设施类型,字典项 + */ + private String appearanceType; + + /** + * 采集/保存文件类型列表,格式:多个采集文件类型,采用","分隔 + */ + private String saveTypeList; + + /** + * 识别类型列表,格式:多个识别类型,采用","分隔 + */ + private String recognitionTypeList; + + /** + * <1>:=A 相<2>:=B 相<3>:=C 相 多个相位,采用","分隔 + */ + private String phase; + + /** + * 备注信息 + */ + private String deviceInfo; + + /** + * 按位定义数据来源,支持 32 位 1 为有效, 0 为无效,如下:第 0 位:摄像机第 1 位:机器人第 2 位:无人机第 3 位:声纹第 4 位:在线监测区域巡视主机上报上级系统填写 + */ + private String dataType; + + /** + * 正常范围下限,巡视主机上报上级系统填写(选填)数值或者百分比 + */ + private String lowerValue; + + /** + * 正常范围上限,巡视主机上报上级系统填写(选填) + */ + private String upperValue; + + /** + * 关联视频编码及预置位,json格式 + */ + private String videoPos; + + /** + * 0:不进行告警;1:进行告警 + */ + private String isAlarm; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * json串,包含设备编号、预置位号、识别类型、文件类型、上限、下限 + * { "device_name":"摄像头名称","device_code":"摄像头编码","channel_code":"视频通道编码","device_pos":"预置位号" } + * + */ + private String patroldeviceJson ; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 是否识别实物ID,默认0:不识别;1:识别 + */ + @TableField("identify_material_id") + private String identifyMaterialId; + + /** + * 预警区间最小值 + */ + private Double earlyMin; + + /** + * 预警区间最大值 + */ + private Double earlyMax; + + /** + * 一般区间最小值 + */ + private Double sameMin; + + /** + * 一般区间最大值 + */ + private Double sameMax; + + /** + * 严重区间最小值 + */ + private Double seriousMin; + + /** + * 严重区间最大值 + */ + private Double seriousMax; + + /** + * 危急区间最小值 + */ + private Double criticalMin; + + /** + * 危急区间最大值 + */ + private Double criticalMax; + + /** + * 识别类型列表,格式:多个识别类型名称,采用","分隔 + */ + private String recognitionTypeNameList; + + /** + * 表计量程角度,{min_temp:0,max_temp:2.5,min_angle:170,max_angle} + */ + private String outsideAngle; + + /** + * 单位 + */ + private String unit; + + /** + * 分析类型、缺陷类型、判别类型List集合,采用","分隔 + */ + private String pictureAnalysisTypeList; + + /** + * 巡视点位表表计特征 + */ + private String outsideFeature; + + /** + * 点位分类:字典项;1:Ⅰ类;2:Ⅱ类;3:Ⅲ类 + */ + private String deviceClass; + + /** + * 模板图片地址 + */ + private String imageNormalUrlpath; + + /** + * 延时报警时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime delayedAlarmDate; + + /** + * 生效区域 + */ + private String effectiveArea; +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationMaindevice.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationMaindevice.java new file mode 100644 index 0000000..55871e7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationMaindevice.java @@ -0,0 +1,111 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站_主设备 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_maindevice") +public class SubstationMaindevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主设备id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String mainDeviceId; + + /** + * 主设备类型 + */ + private String deviceType; + + /** + * 主设备名称 + */ + private String mainDeviceName; + + /** + * 间隔id + */ + private String bayId; + + /** + * 间隔名称 + */ + private String bayName; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 区域id + */ + private String areaId; + + /** + * 区域名称 + */ + private String areaName; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1 + */ + private Integer custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 实物ID + */ + @TableField("material_id") + private String materialId; +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationModel.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationModel.java new file mode 100644 index 0000000..ce35db4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationModel.java @@ -0,0 +1,89 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 变电站-三维动态模型 + *

+ * + * @author zhengsl + * @since 2024-04-05 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_model") +public class SubstationModel implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private String id; + + /** + * 类型:Camera,DevicePoint,UV,UAV,UAVLine,EquipmentComponent + */ + private String type; + + /** + * 模型对象名称 + */ + private String objname; + + /** + * 位置坐标 + */ + private String coordinate; + + /** + * 旋转角度 + */ + private String rotation; + + /** + * 模型对象-分类:camera1, + */ + private String modelType; + + /** + * 关联对象信息 + */ + private String objinfo; + + /** + * 构成路线的点 + */ + private String linePoints; + + /** + * 路线关联的巡视点位 + */ + private String checkPoints; + + /** + * 变电站ID + */ + private String stationId; + + /** + * 备用1 + */ + private Integer custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationPatroldevice.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationPatroldevice.java new file mode 100644 index 0000000..ce09eb7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/SubstationPatroldevice.java @@ -0,0 +1,283 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 变电站_巡视设备 + *

+ * + * + * @since 2023-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_substation_patroldevice") +public class SubstationPatroldevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 巡视设备ID + */ + @TableId(type = IdType.ASSIGN_UUID) + private String patroldeviceId; + + /** + * 摄像机编号 + */ + private String patroldeviceCode; + + /** + * 摄像机名称 + */ + private String patroldeviceName; + + /** + * 变电站id + */ + private String stationId; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 变电站编码 + */ + private String stationCode; + + /** + * 区域id,可为空 + */ + private String areaId; + + /** + * 区域名称,可为空 + */ + private String areaName; + + /** + * 间隔id,可为空 + */ + //private String bayId; + + /** + * 间隔名称,可为空 + */ + //private String bayName; + + /** + * 设备型号 + */ + private String deviceModel; + + /** + * 生产厂家 + */ + private String manufacturer; + + /** + * 使用单位 + */ + private String useUnit; + + /** + * 设备来源 + */ + private String deviceSource; + + /** + * 生产日期 + */ + private String productionDate; + + /** + * 出厂编号 + */ + private String productionCode; + + /** + * 当设备类型为机器人时传递该字段<0>: = 不轮转<1>: = 轮转 + */ + private String istransport; + + /** + * 当设备类型为摄像机时传递该字段<10>: = 枪机<11>: = 球机<12>: = 云台 + */ + private String useMode; + + /** + * <1>: = 可见光<2>: = 红外<3>: = 可见光与红外 + */ + private String videoMode; + + /** + * 视频设备的安装位置,其余种类的巡视设 备传空值 + */ + private String place; + + /** + * <1>: = 室外轮式机器人<2>: = 室内轮式机器人<3>: = 挂轨机器人<10>: = 摄像机<11>: = 硬盘录像机<12>: = 智能分析主机<13>: = 无人机<14>: = 声纹<15>: = 无人机机巢<20>: = 区域巡视主机<21>: = 边缘节点 + */ + private String type; + + /** + * 用于描述巡视设备的文字信息 + */ + private String patroldeviceInfo; + + /** + * 当视频设备为机器人上挂载的,存储机器 人编码,否则为空 + */ + private String robotsCode; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + + + /** + * 以太网IP地址 + */ + private String ipaddr; + + /** + * 以太网Mac地址 + */ + private String macaddr; + + /** + * 默认0,不进行发送 + */ + private String heartBeatInterval; + + /** + * 默认0,不进行发送 + */ + private String patroldeviceRunInterval; + + /** + * 默认0,不进行发送 + */ + private String nestRunInterval; + + /** + * 默认0,不进行发送 + */ + private String weatherInterval; + + + /** + * Json扩展的摄像头通道信息 + */ + private String channelinfo; + + + /** + * 默认null,不进行静默监视,字典项,多个识别类型值,","隔开 + */ + private String slienceType; + + /** + * 静默监视配置参数[{type:"",frame_interval:"",alarm_frequency:""}] + */ + private String slienceParams; + + /** + * 守望预置位 + */ + private String preset; + + /** + * 守望位回归时间,默认10,单位分钟 + */ + @TableField("presettime") + private String presetTime; + + + /** + * 预置位变动时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime changepresettime; + + /** + * 实物id,只能输入数字 + */ + @TableField("material_id") + private String materialId; + + /** + * 国际编码,只能输入数字 + */ + @TableField("internationalId") + private String internationalId; + + /** + * 生产国家 + */ + private String productionCountry; + + /** + * 是否在线,1为在线,0为离线 + */ + private String online; + + /** + * 摄像头rtsp 推流地址 + */ + private String rtspurl; + + + /** + * 是否任务执行中 0 - 否 1- 是 + */ + private String working; + + + + /** + * 是否承担静默任务 0 - 否 1- 是 + */ + private String dailymonitor; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/TreeNode.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/TreeNode.java new file mode 100644 index 0000000..c5f3129 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/TreeNode.java @@ -0,0 +1,29 @@ +package com.yfd.platform.modules.basedata.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Date: 2024/4/18 15:10 + * @Description: + */ +@Data +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@NoArgsConstructor +public class TreeNode { + + private String id; + private String name; + private List children = new ArrayList<>(); + + public TreeNode(String id, String name) { + this.id = id; + this.name = name; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/WeatherLog.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/WeatherLog.java new file mode 100644 index 0000000..92fbf91 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/domain/WeatherLog.java @@ -0,0 +1,113 @@ +package com.yfd.platform.modules.basedata.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 微气象数据日志表 + *

+ * + * + * @since 2023-04-27 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_weather_log") +public class WeatherLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private String id; + + /** + * 变电站id + */ + private String stationId; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 变电站编码 + */ + private String stationCode; + + /** + * 巡视设备id,微气象信息由机器人进行采集,所以关联机器人信息 + */ + private String patroldeviceId; + + /** + * 巡视设备编码 + */ + private String patroldeviceCode; + + /** + * 巡视设备名称 + */ + private String patroldeviceName; + + /** + * 时间 + */ + private String time; + + /** + * 类型,1:环境温度,2:环境湿度,3:风速,4:雨量,5:风向,6:气压,7:氧气,8:SF6 + */ + private String type; + + /** + * 值 + */ + private String value; + + /** + * 单位 + */ + private String unit; + + /** + * 数据状态,0:为过期数据,1:为最新数据,同类型数据最新数据保持一条 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/AlgorithmManufacturerVersionMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/AlgorithmManufacturerVersionMapper.java new file mode 100644 index 0000000..c23a972 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/AlgorithmManufacturerVersionMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.AlgorithmManufacturerVersion; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zhengsl + * @since 2024-05-06 + */ +public interface AlgorithmManufacturerVersionMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/AlgorithmModelMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/AlgorithmModelMapper.java new file mode 100644 index 0000000..45d6f89 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/AlgorithmModelMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; + +/** + *

+ * 算法模型表 Mapper 接口 + *

+ * + * + * @since 2023-08-25 + */ +public interface AlgorithmModelMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/DeviceChannelMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/DeviceChannelMapper.java new file mode 100644 index 0000000..c4b6d96 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/DeviceChannelMapper.java @@ -0,0 +1,22 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 设备下的通道信息(每个设备查看视频的主要信息表) + Mapper 接口 + *

+ * + * + * @since 2023-04-21 + */ +public interface DeviceChannelMapper extends BaseMapper { + + List> selectDeviceByCode(String codes); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/LinkageSignalMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/LinkageSignalMapper.java new file mode 100644 index 0000000..61d7284 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/LinkageSignalMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; + +/** + *

+ * 联动信号关联表 Mapper 接口 + *

+ * + * + * @since 2023-04-06 + */ +public interface LinkageSignalMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/PatroldeviceResumeMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/PatroldeviceResumeMapper.java new file mode 100644 index 0000000..e7576d5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/PatroldeviceResumeMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.PatroldeviceResume; + +/** + *

+ * 机器人及无人机履历表 Mapper 接口 + *

+ * + * + * @since 2023-04-26 + */ +public interface PatroldeviceResumeMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationAreaMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationAreaMapper.java new file mode 100644 index 0000000..709debb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationAreaMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.SubstationArea; + +/** + *

+ * 变电站_区域 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationAreaMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationBayMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationBayMapper.java new file mode 100644 index 0000000..2acff8b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationBayMapper.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.SubstationBay; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站_间隔 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationBayMapper extends BaseMapper { + + List> getSubstationBayInfo(String stationId); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationComponentMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationComponentMapper.java new file mode 100644 index 0000000..9cfacfa --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationComponentMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; + +/** + *

+ * 变电站_设备部件 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationComponentMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationDeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationDeviceMapper.java new file mode 100644 index 0000000..b2dca50 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationDeviceMapper.java @@ -0,0 +1,29 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站_巡视点位 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationDeviceMapper extends BaseMapper { + + List getDeviceByCode(String patrolDeviceCode); + + List> getDeviceByJson(String internationalId); + + List> selectByIds(String ids); + List selectByIdList(String ids); + + String selectNextUavDeviceCode(); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationMaindeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationMaindeviceMapper.java new file mode 100644 index 0000000..0d25a3a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationMaindeviceMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.SubstationMaindevice; + +/** + *

+ * 变电站_主设备 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationMaindeviceMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationMapper.java new file mode 100644 index 0000000..85f2fda --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationMapper.java @@ -0,0 +1,22 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.Substation; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + *

+ * 变电站 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationMapper extends BaseMapper { + @Select(value = {""}) + List selectByStaionCode(String stationcode); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationModelMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationModelMapper.java new file mode 100644 index 0000000..16b8197 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationModelMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.yfd.platform.modules.basedata.domain.SubstationModel; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 变电站-三维动态模型 Mapper 接口 + *

+ * + * @author zhengsl + * @since 2024-04-05 + */ +public interface SubstationModelMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationPatroldeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationPatroldeviceMapper.java new file mode 100644 index 0000000..e9c8d46 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/SubstationPatroldeviceMapper.java @@ -0,0 +1,25 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站_巡视设备 Mapper 接口 + *

+ * + * + * @since 2023-03-28 + */ +public interface SubstationPatroldeviceMapper extends BaseMapper { + + Page> getCameraStat(Page> page, String stationId, String patrolDeviceName, String channel, String flag); + + List> getCameraStatReport(String stationCode); + + List> getDockInfoBySn(String dockSn); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/WeatherLogMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/WeatherLogMapper.java new file mode 100644 index 0000000..4710ef9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/mapper/WeatherLogMapper.java @@ -0,0 +1,19 @@ +package com.yfd.platform.modules.basedata.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.basedata.domain.WeatherLog; + +/** + *

+ * 微气象数据日志表 Mapper 接口 + *

+ * + * + * @since 2023-04-27 + */ +public interface WeatherLogMapper extends BaseMapper { + + void deleteWeatherLog(); + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IAlgorithmManufacturerVersionService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IAlgorithmManufacturerVersionService.java new file mode 100644 index 0000000..ff2e6e4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IAlgorithmManufacturerVersionService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.AlgorithmManufacturerVersion; + +/** + *

+ * 服务类 + *

+ * + * @author zhengsl + * @since 2024-05-06 + */ +public interface IAlgorithmManufacturerVersionService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IAlgorithmModelService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IAlgorithmModelService.java new file mode 100644 index 0000000..b7be1f2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IAlgorithmModelService.java @@ -0,0 +1,55 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + *

+ * 算法模型表 服务类 + *

+ * + * + * @since 2023-08-25 + */ +public interface IAlgorithmModelService extends IService { + + /********************************** + * 用途说明: 上传文件 + * 参数说明 file 文件域 + * 返回值说明: java.lang.String + ***********************************/ + void uploadModelFile(MultipartFile file,String modelId); + + /********************************** + * 用途说明: 预览模型文件 + * 参数说明 fileName 文件名称 + * 参数说明 response + * 返回值说明: void + ***********************************/ + void viewModelFile(String fileName, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 删除模型文件 + * 参数说明 id 模型id + * 参数说明 fileName 文件名称 + * 返回值说明: boolean + ***********************************/ + boolean deleteModelFile(String id, String fileName); + + /********************************** + * 用途说明: 获取当前该区域某站点的运行算法参数 + * 参数说明 sectionId 区域巡视主机编码 + * 参数说明 stationId 变电站编码 + * 参数说明 type + * 返回值说明: void + ***********************************/ + void getRecogParams(String sectionId, String stationId, String type); + + void getHistoryVersion(String sectionId, String stationId, String type, String algorithmManufacturer); + + void updateAlgorithmVersion(String sectionId, String stationId, String type, String version); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IDeviceChannelService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IDeviceChannelService.java new file mode 100644 index 0000000..9355200 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IDeviceChannelService.java @@ -0,0 +1,17 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; + +/** + *

+ * 设备下的通道信息(每个设备查看视频的主要信息表) + 服务类 + *

+ * + * + * @since 2023-04-21 + */ +public interface IDeviceChannelService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ILinkageSignalService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ILinkageSignalService.java new file mode 100644 index 0000000..555f9a0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ILinkageSignalService.java @@ -0,0 +1,41 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.component.nettyudpserver.MyMessageProtocol; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.text.ParseException; + +/** + *

+ * 联动信号关联表 服务类 + *

+ * + * + * @since 2023-04-06 + */ +public interface ILinkageSignalService extends IService { + + /********************************** + * 用途说明: 接收文件 + * 参数说明 link + * 返回值说明: void + ***********************************/ + void receiveFile(MultipartFile file, String type) throws IOException, ParseException; + + /********************************** + * 用途说明: 创建联动任务并且立即执行 + * 参数说明 link + * 返回值说明: void + ***********************************/ + void createLinkTask(String message) throws ParseException; + + /********************************** + * 用途说明: 根据一键顺控指令创建巡视任务 + * 参数说明 + * 返回值说明: + ***********************************/ + boolean createLinkSiginTask(MyMessageProtocol msg) throws ParseException, InterruptedException; +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IPatroldeviceResumeService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IPatroldeviceResumeService.java new file mode 100644 index 0000000..8f25c19 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IPatroldeviceResumeService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.PatroldeviceResume; + +/** + *

+ * 机器人及无人机履历表 服务类 + *

+ * + * + * @since 2023-04-26 + */ +public interface IPatroldeviceResumeService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationAreaService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationAreaService.java new file mode 100644 index 0000000..983e537 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationAreaService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.SubstationArea; + +/** + *

+ * 变电站_区域 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationAreaService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationBayService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationBayService.java new file mode 100644 index 0000000..a9f2d19 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationBayService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.SubstationBay; + +/** + *

+ * 变电站_间隔 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationBayService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationComponentService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationComponentService.java new file mode 100644 index 0000000..7991a46 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationComponentService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; + +/** + *

+ * 变电站_设备部件 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationComponentService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationDeviceService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationDeviceService.java new file mode 100644 index 0000000..24aa6e4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationDeviceService.java @@ -0,0 +1,75 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.TreeNode; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站_巡视点位 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationDeviceService extends IService { + + /********************************** + * 用途说明: 删除巡视点位 + * 参数说明 deviceId 巡视点位id + * 返回值说明: boolean + ***********************************/ + boolean deleteSubstationDeviceById(String deviceId); + + /********************************** + * 用途说明: 设置巡视点位状态是否启用 + * 参数说明 deviceId 巡视点位id + * 参数说明 dataStatus 巡视点位状态 + * 返回值说明: boolean + ***********************************/ + boolean setSubstationDeviceStatus(String deviceId, String dataStatus); + + /********************************** + * 用途说明: 获取类型 + * 参数说明 + * 返回值说明: java.util.List>> + ***********************************/ + Map>> getDeviceType(); + + /*********************************** + * 用途说明:保存图片模板 + * 参数说明 + * deviceId 点位ID + * multipartFile 文件对象 + * 返回值说明: 文件名 + ************************************/ + String saveImgTemplate(HttpServletResponse response, String deviceId, String devicecode, String channelcode) throws Exception; + + List getDeviceByCode(String patrolDeviceCode); + + String uploadExcel(MultipartFile file) throws Exception; + + /********************************** + * 用途说明: 导出点位 + * 参数说明 ids 点位id + * 返回值说明: void + ***********************************/ + void deviceDownloadFile(Page> page, String bayId, + String componentId, String mainDeviceId, String deviceName, HttpServletResponse response) throws IOException; + + List getComponentTree(String bayId,String deviceName); + + TreeNode getBayTree(String stationCode, String deviceName); + + List> getDeviceByJson(String internationalId); + + String selectNextUavDeviceCode(); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationMaindeviceService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationMaindeviceService.java new file mode 100644 index 0000000..b566585 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationMaindeviceService.java @@ -0,0 +1,126 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; +import com.yfd.platform.modules.basedata.domain.SubstationMaindevice; +import com.yfd.platform.modules.basedata.domain.TreeNode; +import com.yfd.platform.system.domain.SysDictionaryItems; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站_主设备 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationMaindeviceService extends IService { + + /********************************** + * 用途说明: 获取主设备及部件树 + * 参数说明 name 参数名称 + * 返回值说明: java.util.List> + ***********************************/ + List> getMainDeviceTree(String bayId, String name); + + /********************************** + * 用途说明: 修改主设备状态 + * 参数说明 id 主设备id + * 参数说明 dataStatus 修改的状态 + * 返回值说明: boolean + ***********************************/ + boolean setMainDeviceStatus(String id, String dataStatus); + + /********************************** + * 用途说明: 删除主设备 + * 参数说明 id 主设备id + * 返回值说明: boolean + ***********************************/ + boolean deleteMainDevice(String id); + + /********************************** + * 用途说明: 新增部件 + * 参数说明 substationComponent 部件对象 + * 返回值说明: boolean + ***********************************/ + boolean addComponent(SubstationComponent substationComponent); + + /********************************** + * 用途说明: 根据Id获取部件 + * 参数说明 componentId 部件id + * 返回值说明: com.yfd.platform.basedata.domain.SubstationComponent + ***********************************/ + SubstationComponent getComponentById(String componentId); + + /********************************** + * 用途说明: 修改部件 + * 参数说明 substationComponent 部件对象 + * 返回值说明: boolean + ***********************************/ + boolean updateComponent(SubstationComponent substationComponent); + + /********************************** + * 用途说明: 修改部件状态 + * 参数说明 componentId 部件id + * 参数说明 dataStatus 修改的状态 + * 返回值说明: boolean + ***********************************/ + boolean setComponentStatus(String componentId, String dataStatus); + + /********************************** + * 用途说明: 删除部件 + * 参数说明 componentId 部件id + * 返回值说明: boolean + ***********************************/ + boolean deleteComponent(String componentId); + + /********************************** + * 用途说明: 导入文件 + * 参数说明 file + * 参数说明 type 主设备,部件 + * 返回值说明: java.lang.String + ***********************************/ + String uploadExcel(MultipartFile file, String type) throws Exception; + + /********************************** + * 用途说明: 获取部件 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + List> getComponent(String mainDeviceId,String bayId,String componentName); + + /********************************** + * 用途说明: 获取当前主设备类型 + * 参数说明 deviceType + * 返回值说明: com.yfd.platform.system.domain.SysDictionaryItems + ***********************************/ + SysDictionaryItems getMainDeviceType(String deviceType); + + /********************************** + * 用途说明: 获取主设备类型集合 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + List> getDeviceType(); + + /********************************** + * 用途说明: 获取间隔下的部件 + * 参数说明 bayId 间隔Id + * 返回值说明: java.util.List> + ***********************************/ + List> getComponentByBay(String bayId); + + + /********************************** + * 用途说明: 获取主设备导航树 + * 参数说明 stationCode + * 返回值说明: java.util.List> + ***********************************/ + List> getSubstationMainDeviceTree(String stationCode,String mainDeviceName); + + TreeNode getSubstationMainTree(String stationCode, String mainDeviceName); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationModelService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationModelService.java new file mode 100644 index 0000000..5ccad93 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationModelService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.basedata.service; + +import com.yfd.platform.modules.basedata.domain.SubstationModel; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 变电站-三维动态模型 服务类 + *

+ * + * @author zhengsl + * @since 2024-04-05 + */ +public interface ISubstationModelService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationPatroldeviceService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationPatroldeviceService.java new file mode 100644 index 0000000..06cb383 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationPatroldeviceService.java @@ -0,0 +1,147 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 变电站_巡视设备 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationPatroldeviceService extends IService { + + /********************************** + * 用途说明: 删除巡视设备 + * 参数说明 id 巡视设备id + * 返回值说明: boolean + ***********************************/ + boolean deleteSubstationDeviceById(String id); + + /********************************** + * 用途说明: 设置巡视点位状态 + * 参数说明 id 巡视设备id + * 参数说明 dataStatus 设备状态 + * 返回值说明: boolean + ***********************************/ + boolean setSubstationDeviceStatus(String id, String dataStatus); + + /********************************** + * 用途说明: 根据用户id查询所有摄像头id + * 参数说明 id 用户id + * 返回值说明: java.util.List + ***********************************/ + List getCameraIds(String id); + + /********************************** + * 用途说明: 新增或修改机器人 + * 参数说明 substationPatroldevices + * 返回值说明: void + ***********************************/ + void addOrUpdatePatrolRobot(List substationPatroldevices); + + /********************************** + * 用途说明: 对摄像头添加静默监视任务 + * 参数说明 + * 返回值说明: 无 + ***********************************/ + void addDailyMonitorTask(); + + + /********************************** + * 用途说明: 设置静默监视是否启用 + * 参数说明 + * 返回值说明: void + ***********************************/ + boolean setDailyMonitorStatus(String id, String dailyMonitor); + + + /** + * 判断摄像机是否处于巡视任务状态 + * + * @param stationcode 变电站编号 + * @param devicecode 摄像机编号 + * @return String 是否工作中 + */ + String isWorkingofPatroldevice(String stationcode,String devicecode); + + /********************************** + * 用途说明: 摄像机录像完整率统计 + * 参数说明 page + * 参数说明 stationId + * 参数说明 patrolDeviceName + * 参数说明 channel + * 参数说明 flag + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getCameraIntegrityRate(Page> page, String stationId, String patrolDeviceName, String channel, String flag); + + Page> getCameraStat(Page> page, String stationId, String patrolDeviceName, String channel, String flag); + + List> getCameraStat(String stationCode); + + /********************************** + * 用途说明: 单个摄像机完整率 + * 参数说明 channelId + * 返回值说明: java.util.Map + ***********************************/ + String getCameraStatByCode(String channelId); + + /********************************** + * 用途说明: 巡视设备累计运行天数 + * 参数说明 page + * 参数说明 stationId + * 参数说明 patrolDeviceName + * 参数说明 patrolDeviceCode + * 参数说明 type + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getRobotOnlineRate(Page> page, String stationId, String patrolDeviceName, String patrolDeviceCode, String type); + + /********************************** + * 用途说明: 巡视设备累计运行天数 + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getRobotOnlineRate(); + + /********************************** + * 用途说明: 新增巡视设备 + * 参数说明 patrolDeviceInfo + * 返回值说明: boolean + ***********************************/ + boolean addPatrolDevice(String patrolDeviceInfo); + + /********************************** + * 用途说明: 设置静默监视生效区域 + * 参数说明 patrolDeviceInfo + * 返回值说明: boolean + ***********************************/ + boolean setEffectiveArea(String type, String id,String effecttivearea); + + /********************************** + * 用途说明: 导出巡视设备台账 + * 参数说明 stationId 变电站id + * 参数说明 type 设备类型 + * 参数说明 patrolDeviceName 设备名称 + * 参数说明 deviceModel 设备型号 + * 参数说明 manufacturer 生产厂家 + * 参数说明 response + * 返回值说明: void + ***********************************/ + void exportPatrolDeviceList(String stationId, String type, String patrolDeviceName, String deviceModel, + String manufacturer, HttpServletResponse response) throws IOException; + + DeviceChannel queryChannelByDeviceIp(String ipaddr); + + List> getDockInfoBySn(String dockSn); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationService.java new file mode 100644 index 0000000..a39b202 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/ISubstationService.java @@ -0,0 +1,137 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.Substation; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * 变电站 服务类 + *

+ * + * + * @since 2023-03-28 + */ +public interface ISubstationService extends IService { + + /********************************** + * 用途说明: 获取变电站区域间隔导航树 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + List> getSubstationTree(String stationId); + + /********************************** + * 用途说明: 获取变电站树 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + List> getSubstationBayTree(String name, Set orgCodeByUser); + + /*********************************** + * 用途说明:上传图片 + * 参数说明 + * image 图片 + * 返回值说明: 是否上传成功 + ***********************************/ + String uploadIcon(MultipartFile image); + + /********************************** + * 用途说明: 删除图片 + * 参数说明 id id + * 参数说明 filename 图片名称 + * 返回值说明: boolean + ***********************************/ + boolean deleteImage(String id, String filename); + + /********************************** + * 用途说明: 删除变电站 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + boolean deleteSubstation(String stationId); + + /********************************** + * 用途说明: 删除区域 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + boolean deleteSubstationArea(String areaId); + + /********************************** + * 用途说明: 删除间隔 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + boolean deleteSubstationBay(String bayId); + + /********************************** + * 用途说明: 修改变电站状态 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + boolean setSubstationStatus(String stationId, String dataStatus); + + /********************************** + * 用途说明: 修改区域状态 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + boolean setSubstationAreaStatus(String areaId, String dataStatus); + + /********************************** + * 用途说明: 修改间隔状态 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + boolean setSubstationBayStatus(String bayId, String dataStatus); + + /********************************** + * 用途说明: 导入文件 + * 参数说明 file + * 返回值说明: java.lang.String + ***********************************/ + String uploadExcel(MultipartFile file, String type) throws Exception; + + /********************************** + * 用途说明: 取消修改 + * 参数说明 id images + * 返回值说明: java.lang.String + ***********************************/ + boolean cancelImage(String id, String images); + + /********************************** + * 用途说明: 获取变电站区域导航树 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + List> getSubstationAreaTree(String stationId); + + /********************************** + * 用途说明: 获取区域下所有间隔 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + List> getSubstationBayByArea(String stationCode, String areaId, String bayName); + + /********************************** + * 用途说明: 修改变电站 + * 参数说明 substation + * 返回值说明: boolean + ***********************************/ + boolean updateSubstation(Substation substation); + + + /********************************** + * 用途说明: 查询变电站状态 + * 参数说明 stationcode 变电站编号, + * online:1=在线,0-离线 + * 返回值说明: boolean + ***********************************/ + int getStationOnline(String stationcode); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IWeatherLogService.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IWeatherLogService.java new file mode 100644 index 0000000..d6c0182 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/IWeatherLogService.java @@ -0,0 +1,24 @@ +package com.yfd.platform.modules.basedata.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.WeatherLog; + +import java.util.Map; + +/** + *

+ * 微气象数据日志表 服务类 + *

+ * + * + * @since 2023-04-27 + */ +public interface IWeatherLogService extends IService { + + /********************************** + * 用途说明: 查询环境信息 + * 参数说明 stationId + * 返回值说明: java.util.Map + ***********************************/ + Map getWeatherLogList(String stationId); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/AlgorithmManufacturerVersionServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/AlgorithmManufacturerVersionServiceImpl.java new file mode 100644 index 0000000..d1178cd --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/AlgorithmManufacturerVersionServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.AlgorithmManufacturerVersion; +import com.yfd.platform.modules.basedata.mapper.AlgorithmManufacturerVersionMapper; +import com.yfd.platform.modules.basedata.service.IAlgorithmManufacturerVersionService; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author zhengsl + * @since 2024-05-06 + */ +@Service +public class AlgorithmManufacturerVersionServiceImpl extends ServiceImpl implements IAlgorithmManufacturerVersionService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/AlgorithmModelServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/AlgorithmModelServiceImpl.java new file mode 100644 index 0000000..76d4318 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/AlgorithmModelServiceImpl.java @@ -0,0 +1,160 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.component.nettyclient.*; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.modules.basedata.mapper.AlgorithmModelMapper; +import com.yfd.platform.modules.basedata.service.IAlgorithmModelService; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.utils.FileUtil; +import io.netty.util.CharsetUtil; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + *

+ * 算法模型表 服务实现类 + *

+ * + * + * @since 2023-08-25 + */ +@Service +public class AlgorithmModelServiceImpl extends ServiceImpl implements IAlgorithmModelService { + + @Resource + private HttpServerConfig httpServerConfig; + + @Resource + private RedisTemplate redisTemplate; + + @Resource + private IPlatformParentSystemService platformParentSystemService; + + /********************************** + * 用途说明: 上传文件 + * 参数说明 file 文件域 + * 返回值说明: java.lang.String + ***********************************/ + @Override + public void uploadModelFile(MultipartFile file, String modelId) { + // 文件存储地址 + String fileName = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(file.getOriginalFilename()); + // 上传文件 + String name = + Objects.requireNonNull(FileUtil.upload(file, httpServerConfig.getDocumentPath(), fileName)).getName(); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AlgorithmModel::getId, modelId).set(AlgorithmModel::getCustom1, name); + this.update(updateWrapper); + } + + /********************************** + * 用途说明: 预览模型文件 + * 参数说明 fileName 文件名称 + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void viewModelFile(String fileName, HttpServletResponse response) throws IOException { + boolean exist = FileUtil.exist(httpServerConfig.getDocumentPath() + fileName); + if (!exist) { + throw new RuntimeException("文件不存在"); + } + FileUtil.viewPDF(httpServerConfig.getDocumentPath() + fileName, response); + } + + /********************************** + * 用途说明: 删除模型文件 + * 参数说明 id 模型id + * 参数说明 fileName 文件名称 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteModelFile(String id, String fileName) { + boolean exist = FileUtil.exist(httpServerConfig.getDocumentPath() + fileName); + if (!exist) { + throw new RuntimeException("删除失败"); + } + FileUtil.del(httpServerConfig.getDocumentPath() + fileName); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AlgorithmModel::getId, id).set(AlgorithmModel::getCustom1, ""); + return this.update(updateWrapper); + + } + + @Override + public void getRecogParams(String sectionId, String stationId, String type) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("section_id", sectionId); + jsonObject.putOnce("station_id", stationId); + jsonObject.putOnce("type", type); + this.sendAlgorithmData(SystemCode.TYPE_ALGORITHM_PARAM_CODE.getCode(), "", "", jsonObject.toString()); + } + + @Override + public void getHistoryVersion(String sectionId, String stationId, String type, String algorithmManufacturer) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("section_id", sectionId); + jsonObject.putOnce("station_id", stationId); + jsonObject.putOnce("type", type); + jsonObject.putOnce("algorithm_manufacturer", algorithmManufacturer); + this.sendAlgorithmData(SystemCode.TYPE_ALGORITHM_VERSION_CODE.getCode(), "", "", jsonObject.toString()); + } + + @Override + public void updateAlgorithmVersion(String sectionId, String stationId, String type, String version) { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("section_id", sectionId); + jsonObject.putOnce("station_id", stationId); + jsonObject.putOnce("type", type); + jsonObject.putOnce("version", version); + this.sendAlgorithmData(SystemCode.TYPE_ALGORITHM_UPDATE_CODE.getCode(), "", "", jsonObject.toString()); + } + + public void sendAlgorithmData(String Type, String Command, String Code, String Items) { + List list = + platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + if (list.size() <= 0) { + return; + } + PlatformParentSystem platformParentSystem = list.get(0); + String xml = MyXmlUtil.getXml(httpServerConfig.getPatrolServerid(), platformParentSystem.getParentCode(), Type + , Command, Code, Items); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + long requestSessionID = BootSessionCache.getRequestSessionID(); + messageProtocol.setSenderSerialNo(requestSessionID); + redisTemplate.opsForValue().set(Constant.CLOUD_SEND_CODE + requestSessionID, xml); + messageProtocol.setReceiverSerialNo(0L); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if (BootNettyClientChannelCache.channelMapCache.size() > 0) { + for (Map.Entry entry : + BootNettyClientChannelCache.channelMapCache.entrySet()) { + BootNettyClientChannel bootNettyChannel = entry.getValue(); + if (bootNettyChannel != null && bootNettyChannel.getChannel().isOpen()) { + bootNettyChannel.getChannel().writeAndFlush(messageProtocol); + } + } + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/DailyMonitorJob.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/DailyMonitorJob.java new file mode 100644 index 0000000..f5fb5ec --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/DailyMonitorJob.java @@ -0,0 +1,92 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SpringContextHolder; +import lombok.extern.slf4j.Slf4j; + +import java.net.URLEncoder; + + +/** + * @date 2023-05-23 + * 动态执行摄像头静默监视任务 + */ + +@Slf4j +public class DailyMonitorJob implements Runnable { + private String deviceinfo; + private ISubstationPatroldeviceService deviceService; + private HttpRESTfulUtils httpUtil; + private HttpServerConfig serverConfig; + + public DailyMonitorJob(String deviceinfo) { + this.deviceinfo = deviceinfo; + this.deviceService = SpringContextHolder.getBean(ISubstationPatroldeviceService.class); + this.httpUtil = SpringContextHolder.getBean(HttpRESTfulUtils.class); + this.serverConfig = SpringContextHolder.getBean(HttpServerConfig.class); + } + + @Override + public void run() { + log.info(String.format("静默任务开始执行时间:%s", DateUtil.now())); + JSONObject device = JSONUtil.parseObj(deviceinfo); + String isWorking = deviceService.isWorkingofPatroldevice(device.getStr("station_code"), device.getStr( + "patroldevice_code")); + if ("0".equals(isWorking)) {//摄像头不处于工作状态 + //路径命名格式为:变电站编码/年/月/日/巡视任务编码/CCD + String filepath = String.format("%s/%s/", + device.getStr("station_code"), + DateUtil.format(DateUtil.date(), "yyyyMMdd") + ); + //文件命名格式为:时间_区域_摄像头_类型.jpg + String filename = String.format("%s_%s_%s_%s.jpg", + DateUtil.format(DateUtil.date(),"HHmmss"), + device.getStr("area_name"), + device.getStr("place"), + "原图" + ); + try { + String fullfilename = filepath + filename; + String rtslurl = String.format("rtsp://%s:%s/rtp/%s_%s", serverConfig.getMediaIp(), + serverConfig.getMonitorRtspPort(), device.getStr( + "patroldevice_code"), device.getStr("patrolchannel_code")); + if (ObjUtil.isNotEmpty(device.getStr("rtspurl"))) { + rtslurl = device.getStr("rtspurl"); + } + httpUtil.getMonitorVideoSnap(serverConfig.getffmpegPath(), rtslurl, fullfilename); + log.info("完成视频截图并保存到对应位置下!,文件名称:" + fullfilename); // 3、 调用图片识别算法,进行图形识别 + JSONArray typelist = new JSONArray(); + typelist.add(device.getStr("sliencetype")); + fullfilename = URLEncoder.encode(fullfilename, "utf-8");//转码存储 + log.info(String.format("静默任务执行完成时间:%s", DateUtil.now())); + httpUtil.callPicAnalyse(device.getStr("patroldevice_id"), device.getStr("patroldevice_id"), + JSONUtil.toJsonStr(typelist), JSONUtil.toJsonStr(device.getJSONObject("customParams")), + fullfilename); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/DeviceChannelServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/DeviceChannelServiceImpl.java new file mode 100644 index 0000000..52409ec --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/DeviceChannelServiceImpl.java @@ -0,0 +1,21 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; +import com.yfd.platform.modules.basedata.mapper.DeviceChannelMapper; +import com.yfd.platform.modules.basedata.service.IDeviceChannelService; +import org.springframework.stereotype.Service; + +/** + *

+ * 设备下的通道信息(每个设备查看视频的主要信息表) + 服务实现类 + *

+ * + * + * @since 2023-04-21 + */ +@Service +public class DeviceChannelServiceImpl extends ServiceImpl implements IDeviceChannelService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/LinkageSignalServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/LinkageSignalServiceImpl.java new file mode 100644 index 0000000..5d804e5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/LinkageSignalServiceImpl.java @@ -0,0 +1,287 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.component.nettyudpserver.MyMessageProtocol; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.basedata.mapper.LinkageSignalMapper; +import com.yfd.platform.modules.basedata.service.ILinkageSignalService; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.patroltask.service.impl.QuartzMultiTaskManage; +import com.yfd.platform.utils.SecurityUtils; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + *

+ * 联动信号关联表 服务实现类 + *

+ * + * + * @since 2023-04-06 + */ +@Service +public class LinkageSignalServiceImpl extends ServiceImpl implements ILinkageSignalService { + + @Resource + private ITaskService taskService; + + @Resource + private QuartzMultiTaskManage quartztaskManage; + + @Resource + private TaskTodoMapper taskTodoMapper; + + /********************************** + * 用途说明: 接收文件 + * 参数说明 link + * 返回值说明: void + ***********************************/ + @Override + public void receiveFile(MultipartFile file, String type) throws IOException, ParseException { + Reader reader = new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(reader); + br.readLine();//br是迭代器类型的,每readline一次,行数指向next,用于过滤第0行标题 + String line; + while ((line = br.readLine()) != null) { + String trim = line.trim(); + if (!trim.startsWith("#")) { + continue; + } + String[] split = trim.split("\\s+"); + if (split.length == 7 && "1".equals(type)) { + saveLinkageSignal(split); + } + + if (split.length == 7 && "2".equals(type)) { + createTask(split[2]); + } + } + } + + /********************************** + * 用途说明: 创建联动任务并且立即执行 + * 参数说明 link + * 返回值说明: void + ***********************************/ + @Override + public void createLinkTask(String message) throws ParseException { + String trimMessage = message.trim(); + String[] split = trimMessage.split(""); + int count = 0; + StringBuilder data = new StringBuilder(); + List result = new ArrayList<>(); + for (int i = 0; i < split.length; i++) { + String s = split[i]; + if (StrUtil.isBlank(s) && count < 5) { + result.add(data.toString()); + count++; + data = new StringBuilder(); + } else { + data.append(s); + if (i == (split.length - 1)) { + result.add(data.toString()); + } + } + } + createTask(result.get(1)); + } + + /********************************** + * 用途说明: 创建任务并且立即执行 + * 参数说明 link + * 返回值说明: void + ***********************************/ + private void createTask(String controlNum) throws ParseException { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(LinkageSignal::getControlNum, controlNum).eq(LinkageSignal::getDatastatus, "1"); + List list = this.list(queryWrapper); + if (list == null || list.size() == 0) { + throw new RuntimeException("不存在对应的联动信号或者未绑定点位"); + } + LinkageSignal linkageSignal = list.get(0); + Task task = new Task(); + task.setTaskId(IdUtil.fastSimpleUUID()); + task.setStationCode(linkageSignal.getStationCode()); + task.setStationName(linkageSignal.getStationName()); + int uuid = UUID.randomUUID().toString().replaceAll("-", "").hashCode(); + uuid = uuid < 0 ? -uuid : uuid; + task.setTaskCode(Integer.toString(uuid)); + task.setTaskName("联动任务" + uuid); + task.setType("4"); + // 主辅设备联动 + task.setTaskType("4"); + task.setTaskTodoType("1"); + task.setPriority("4"); + task.setDeviceLevel(3); + task.setDeviceList(linkageSignal.getDeviceIdList()); + String currentUsername = "主辅联动"; + task.setLastmodifier(currentUsername); + task.setCreator(currentUsername); + Timestamp timestamp = new Timestamp(DateUtil.current()); + task.setLastmodifydate(timestamp); + String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp); + task.setCreateTime(format); + task.setIsenable("1"); + task.setIsReport("0"); + task.setDatastatus("2"); + task.setControlNum(linkageSignal.getControlNum()); + taskService.save(task); + TaskTodo taskTodo = taskService.createRunNowTask(task); + List list1 = taskTodoMapper.selectList(new LambdaQueryWrapper(). + eq(TaskTodo::getStationCode, taskTodo.getStationCode()). + in(TaskTodo::getTaskState, "2"). + ne(TaskTodo::getTaskTodoId, taskTodo.getTaskTodoId()).orderByDesc(TaskTodo::getPlanStartTime) + ); + for (TaskTodo taskTodo1 : list1) { + if ("2".equals(taskTodo1.getTaskState())) {//执行中 + quartztaskManage.interruptJob(taskTodo1); //中断 + // 汤伟加的逻辑 + taskTodo1.setTaskState("3"); + taskTodo1.setCustom1("3"); //程序控制的中断执行 + taskTodoMapper.updateById(taskTodo1); + } + } + String taskTodoId = taskTodo.getTaskTodoId(); + String taskType = taskTodo.getTaskType(); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("taskTodoId", taskTodoId); + jsonObject.putOnce("taskType", taskType); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), jsonObject.toString()); + //立即执行当前任务 + quartztaskManage.runJobNow(taskTodo); + } + + /********************************** + * 用途说明: 新增联动信号 + * 参数说明 link + * 返回值说明: void + ***********************************/ + private void saveLinkageSignal(String[] link) { + LinkageSignal linkageSignal = new LinkageSignal(); + // 序号 + linkageSignal.setOrderNum(link[0].replace("#", "")); + // 站序号 + linkageSignal.setStationNum(link[1]); + // 监控索引号 + String controlNum = link[2]; + linkageSignal.setControlNum(controlNum); + // 设备名称 + linkageSignal.setLinkageDeviceName(link[3]); + // 设备类型 + linkageSignal.setLinkageDeviceType(link[4]); + // 实物ID + linkageSignal.setMaterialId(link[5]); + linkageSignal.setTime(LocalDateTime.now()); + // 是否联动信号 + linkageSignal.setIsLinkageFlag(link[6]); + linkageSignal.setLastmodifier(SecurityUtils.getCurrentUsername()); + linkageSignal.setLastmodifydate(LocalDateTime.now()); + linkageSignal.setStationId("9b4e9d9f70b2256a69dfcbdecb13c960"); + linkageSignal.setDatastatus("0"); + LinkageSignal linkageSignal1 = + this.getOne(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, + controlNum)); + if (linkageSignal1 == null) { + this.save(linkageSignal); + } else { + String id = linkageSignal1.getId(); + linkageSignal.setId(id); + this.updateById(linkageSignal); + } + + } + + + @Override + public boolean createLinkSiginTask(MyMessageProtocol msg) throws ParseException, InterruptedException { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(LinkageSignal::getControlNum, msg.getMonitorindex()); + List list = this.list(queryWrapper); + if (list == null || list.size() == 0) { + throw new RuntimeException("不存在对应的联动信号或者未绑定点位"); + } + LinkageSignal linkageSignal = list.get(0); + Task task = new Task(); + String uuid = IdUtil.fastSimpleUUID(); + task.setTaskId(uuid); + task.setStationCode(linkageSignal.getStationCode()); + task.setStationName(linkageSignal.getStationName()); + long eventtime = DateUtil.parse(msg.getEventtime(), "yyyy-MM-dd HH:mm:ss.SSS").getTime(); + task.setTaskCode(String.valueOf(msg.getMonitorindex()) + "-" + eventtime); + String taskName = "一键顺控任务-"+DateUtil.format(new Timestamp(eventtime),"MMddHHmmss"); + if ("2".equals(linkageSignal.getLinkageType())) { + taskName = "智能联动任务-" + DateUtil.format(new Timestamp(eventtime), "MMddHHmmss"); + } + task.setTaskName(taskName); + //特殊巡视 + task.setType("2"); + // 主辅设备联动 + task.setTaskType("4"); + //1立即执行 + task.setTaskTodoType("1"); + //优先级最高(4级) + task.setPriority("4"); + + task.setDeviceLevel(3); + task.setDeviceList(linkageSignal.getDeviceIdList()); + String currentUsername = "主辅联动"; + task.setLastmodifier(currentUsername); + task.setCreator(currentUsername); + Timestamp timestamp = new Timestamp(DateUtil.current()); + task.setLastmodifydate(timestamp); + task.setCreateTime(msg.getEventtime()); + task.setIsenable("1"); + task.setIsReport("0"); + task.setDatastatus("2"); + task.setCustom3(msg.getValue()); + // 修改为索引号 + task.setControlNum(String.valueOf(msg.getMonitorindex()));//代用了一键顺控的属性值 + taskService.save(task); + TaskTodo taskTodo = taskService.createRunNowTask(task); + List list1 = taskTodoMapper.selectList(new LambdaQueryWrapper(). + eq(TaskTodo::getStationCode, taskTodo.getStationCode()). + in(TaskTodo::getTaskState, "2"). + ne(TaskTodo::getTaskTodoId, taskTodo.getTaskTodoId()).orderByDesc(TaskTodo::getPlanStartTime) + ); + //立即执行当前任务 + for (TaskTodo taskTodo1 : list1) { + if ("2".equals(taskTodo1.getTaskState())) {//执行中 + quartztaskManage.interruptJob(taskTodo1); //中断 + // 汤伟加的逻辑 + taskTodo1.setTaskState("3"); + taskTodo1.setCustom1("3"); //程序控制的中断执行 + taskTodoMapper.updateById(taskTodo1); + } + } + //立即执行当前任务 + quartztaskManage.addJob(taskTodo); +// JSONObject jsonObject = new JSONObject(); +// jsonObject.putOnce("taskTodoId", taskTodo.getTaskTodoId()); +// jsonObject.putOnce("taskType", taskTodo.getTaskType()); +// WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), jsonObject.toString()); + return true; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/PatroldeviceResumeServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/PatroldeviceResumeServiceImpl.java new file mode 100644 index 0000000..0976825 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/PatroldeviceResumeServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.PatroldeviceResume; +import com.yfd.platform.modules.basedata.mapper.PatroldeviceResumeMapper; +import com.yfd.platform.modules.basedata.service.IPatroldeviceResumeService; +import org.springframework.stereotype.Service; + +/** + *

+ * 机器人及无人机履历表 服务实现类 + *

+ * + * + * @since 2023-04-26 + */ +@Service +public class PatroldeviceResumeServiceImpl extends ServiceImpl implements IPatroldeviceResumeService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationAreaServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationAreaServiceImpl.java new file mode 100644 index 0000000..e866019 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationAreaServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.SubstationArea; +import com.yfd.platform.modules.basedata.mapper.SubstationAreaMapper; +import com.yfd.platform.modules.basedata.service.ISubstationAreaService; +import org.springframework.stereotype.Service; + +/** + *

+ * 变电站_区域 服务实现类 + *

+ * + * + * @since 2023-03-28 + */ +@Service +public class SubstationAreaServiceImpl extends ServiceImpl implements ISubstationAreaService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationBayServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationBayServiceImpl.java new file mode 100644 index 0000000..1dc7f76 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationBayServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.SubstationBay; +import com.yfd.platform.modules.basedata.mapper.SubstationBayMapper; +import com.yfd.platform.modules.basedata.service.ISubstationBayService; +import org.springframework.stereotype.Service; + +/** + *

+ * 变电站_间隔 服务实现类 + *

+ * + * + * @since 2023-03-28 + */ +@Service +public class SubstationBayServiceImpl extends ServiceImpl implements ISubstationBayService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationComponentServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationComponentServiceImpl.java new file mode 100644 index 0000000..45fd3ab --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationComponentServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; +import com.yfd.platform.modules.basedata.mapper.SubstationComponentMapper; +import com.yfd.platform.modules.basedata.service.ISubstationComponentService; +import org.springframework.stereotype.Service; + +/** + *

+ * 变电站_设备部件 服务实现类 + *

+ * + * + * @since 2023-03-28 + */ +@Service +public class SubstationComponentServiceImpl extends ServiceImpl implements ISubstationComponentService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationDeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationDeviceServiceImpl.java new file mode 100644 index 0000000..60b13fb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationDeviceServiceImpl.java @@ -0,0 +1,872 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.mapper.*; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryMapper; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.VideoToImageUtil; +import freemarker.template.utility.NullArgumentException; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 变电站_巡视点位 服务实现类 + *

+ * + * @since 2023-03-28 + */ +@Service +@Transactional +@Slf4j +public class SubstationDeviceServiceImpl extends ServiceImpl implements ISubstationDeviceService { + + @Resource + private SysDictionaryMapper sysDictionaryMapper; + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + @Resource + private SubstationDeviceMapper substationDeviceMapper; + @Resource + private SubstationAreaMapper substationAreaMapper; + @Resource + private SubstationBayMapper substationBayMapper; + @Resource + private SubstationMapper substationMapper; + + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private SubstationMaindeviceMapper substationMaindeviceMapper; + + @Resource + private SubstationComponentMapper substationComponentMapper; + @Resource + private HttpServerConfig Config; + + /********************************** + * 用途说明: 删除巡视点位 + * 参数说明 deviceId 巡视点位id + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteSubstationDeviceById(String deviceId) { + String[] split = deviceId.split(","); + for (String s : split) { + SubstationDevice substationDevice = this.getById(s); + if ("1".equals(substationDevice.getDatastatus())) { + continue; + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(TaskResult::getDeviceId, s).eq(TaskResult::getFlag, "0").set(TaskResult::getFlag, "6"); + taskResultMapper.update(null, updateWrapper); + this.removeById(s); + } + return true; + } + + /********************************** + * 用途说明: 设置巡视点位状态是否启用 + * 参数说明 deviceId 巡视点位id + * 参数说明 dataStatus 巡视点位状态 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean setSubstationDeviceStatus(String deviceId, String dataStatus) { + SubstationDevice substationDevice = this.getById(deviceId); + substationDevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationDevice.setLastmodifydate(LocalDateTime.now()); + substationDevice.setDatastatus(dataStatus); + return this.updateById(substationDevice); + } + + /********************************** + * 用途说明: 获取类型 + * 参数说明 + * 返回值说明: java.util.List>> + ***********************************/ + @Override + public Map>> getDeviceType() { + List sysDictionaries = sysDictionaryMapper.selectList(null); + Map>> map = new HashMap<>(); + for (SysDictionary sysDictionary : sysDictionaries) { + String dictCode = sysDictionary.getDictCode(); + String id = sysDictionary.getId(); + List> list = + sysDictionaryItemsService.listMaps(new LambdaQueryWrapper().eq(SysDictionaryItems::getDictId, id).select(SysDictionaryItems::getId, SysDictionaryItems::getItemCode, SysDictionaryItems::getDictName, SysDictionaryItems::getCustom2)); + if ("SilentMonitoringType".equals(dictCode)) { + List> listMaps = new ArrayList<>(); + for (Map stringObjectMap : list) { + stringObjectMap.put("checkInfo", false); + listMaps.add(stringObjectMap); + } + map.put(dictCode, listMaps); + } else { + map.put(dictCode, list); + } + + } + return map; + } + + /*********************************** + * 用途说明:保存图片模板 + * 参数说明 + * deviceId 点位ID + * multipartFile 文件对象 + * 返回值说明: 文件名 + ************************************/ + @Override + public String saveImgTemplate(HttpServletResponse response, String deviceId, String devicecode, + String channelcode) throws Exception { + //根据id查询 + SubstationDevice substationDevice = this.getById(deviceId); + // 模板图片地址 + String tempPath = Config.getTempFilePath(); + String imageNormalUrlPath = substationDevice.getImageNormalUrlpath(); + if (StrUtil.isNotBlank(imageNormalUrlPath)) { + String imgDecode = URLDecoder.decode(imageNormalUrlPath, "utf-8"); + String imgName = tempPath + imgDecode; + FileUtil.del(imgName); + } + String tempfilepath = String.format("%s/%s/", + substationDevice.getStationCode(), + DateUtil.format(DateUtil.date(), "yyyyMMdd") + ); + //文件命名格式为:变电站_点位.jpg + String filename = String.format("%s_%s.jpg", + substationDevice.getStationName(), + substationDevice.getDeviceName() + ); + String filePathName = tempfilepath + filename; + String imgPath = tempPath + filePathName; + String url = String.format("rtsp://%s:%s/rtp/%s_%s", Config.getMediaIp(), Config.getMonitorRtspPort(), + devicecode, channelcode); + + VideoToImageUtil.getVideoPicture(Config.getffmpegPath(), url, imgPath); + //修改 模板图片 + substationDevice.setImageNormalUrlpath(URLEncoder.encode(filePathName, "utf-8")); + //修改 最近修改者 + substationDevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + //修改 最近修改日期 + substationDevice.setLastmodifydate(LocalDateTime.now()); + //更新点位表 + this.updateById(substationDevice); + return filePathName; + } + + @Override + public List getDeviceByCode(String patrolDeviceCode) { + + return substationDeviceMapper.getDeviceByCode(patrolDeviceCode); + } + + @Override + public String uploadExcel(MultipartFile file) throws Exception { + if (file == null) { + throw new NullArgumentException(); + } + String filename = file.getOriginalFilename(); + if (filename == null) { + throw new NullArgumentException(); + } + String a = ""; + try { + // 调用解析文件方法 + a = parseRowCell(filename, file.getInputStream()); + return a; + } catch (IOException e) { + throw new Exception(e.getMessage()); + } + + } + + /********************************** + * 用途说明: 导出点位 + * 参数说明 ids 点位id + * 返回值说明: void + ***********************************/ + @Override + public void deviceDownloadFile(Page> page, String bayId, + String componentId, String mainDeviceId, String deviceName, + HttpServletResponse response) throws IOException { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.eq(SubstationDevice::getBayId, bayId); + } + if (StrUtil.isNotBlank(componentId)) { + queryWrapper.eq(SubstationDevice::getComponentId, componentId); + } + if (StrUtil.isNotBlank(mainDeviceId)) { + queryWrapper.eq(SubstationDevice::getMainDeviceId, mainDeviceId); + } + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationDevice::getDeviceName, deviceName); + } + queryWrapper.orderByAsc(SubstationDevice::getDeviceId); + Page> mapPage = this.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + List> mapList = new ArrayList<>(); + // 点位分类 + List> deviceClassList = sysDictionaryItemsService.getDeviceByType("DeviceClass"); + // 主设备类型 + List> equipmentType = sysDictionaryItemsService.getDeviceByType( + "EquipmentType"); + // 标记类型 + List> meterTypeList = sysDictionaryItemsService.getDeviceByType( + "MeterType"); + records.forEach(r -> { + String deviceTypeName = ""; + if (ObjectUtil.isNotEmpty(r.get("deviceType"))) { + for (Map device : equipmentType) { + String deviceType = r.get("deviceType").toString(); + String itemcode = device.get("itemcode").toString(); + if (deviceType.equals(itemcode)) { + deviceTypeName = device.get("dictname").toString(); + break; + } + + } + } + r.put("deviceTypeName", deviceTypeName); + String meterTypeName = ""; + if (ObjectUtil.isNotEmpty(r.get("meterType"))) { + for (Map device : meterTypeList) { + String meterType = r.get("meterType").toString(); + String itemcode = device.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + meterTypeName = device.get("dictname").toString(); + break; + } + + } + } + r.put("meterTypeName", meterTypeName); + String deviceClassName = ""; + if (ObjectUtil.isNotEmpty(r.get("deviceClass"))) { + for (Map device : deviceClassList) { + String deviceClass = r.get("deviceClass").toString(); + String itemcode = device.get("itemcode").toString(); + if (deviceClass.equals(itemcode)) { + deviceClassName = device.get("dictname").toString(); + break; + } + + } + } + r.put("deviceClassName", deviceClassName); + }); + String[] headers = {"巡视点位编号", "巡视点位名称", "所属主设备", "主设备类型", "所属部件", "表记类型", "点位分类"}; + String[] keys = {"deviceCode", "deviceName", "mainDeviceName", "deviceTypeName", "componentName", "meterTypeName", "deviceClassName"}; + FileUtil.excelImg(records, headers, keys, "", "", response); + FileUtil.downloadExcel(mapList, response); + + } + + @Transactional(rollbackFor = Exception.class) + public String parseRowCell(String filename, InputStream inputStream) { + try { + Workbook workbook = null; + // 判断excel的后缀,不同的后缀用不同的对象去解析 + // xls是低版本的Excel文件 + if (filename.endsWith(".xls")) { + workbook = new HSSFWorkbook(inputStream); + } + // xlsx是高版本的Excel文件 + if (filename.endsWith(".xlsx")) { + workbook = new XSSFWorkbook(inputStream); + } + if (workbook == null) { + throw new RuntimeException("上传失败"); + } + + // 取到excel 中的第一张工作表 + Sheet sheet = workbook.getSheetAt(0); + if (sheet == null) { + throw new NullArgumentException(); + } + // 点位分类 + List> deviceClassList = sysDictionaryItemsService.getDeviceByType( + "DeviceClass"); + // 主设备类型 + List> equipmentType = sysDictionaryItemsService.getDeviceByType( + "EquipmentType"); + // 数据来源 + List> dataTypeList = sysDictionaryItemsService.getDeviceByType( + "dataType"); + // 识别类型 + List> recognitionType = sysDictionaryItemsService.getDeviceByType( + "recognition_type"); + // 表计类型 + List> meterTypeList = sysDictionaryItemsService.getDeviceByType( + "MeterType"); + // 图像状态分析类型 + List> pictureAnalysisTypeList = sysDictionaryItemsService.getDeviceByType( + "PictureAnalysisType"); + // 表计特征 + List> outsideFeatureList = sysDictionaryItemsService.getDeviceByType( + "OutsideFeature"); + // 图像缺陷分析类型 + List> pictureDefectAnalysisTypeList = sysDictionaryItemsService.getDeviceByType( + "PictureDefectAnalysisType"); + // 相位类型 + List> devicePhaseList = sysDictionaryItemsService.getDeviceByType( + "DevicePhase"); + // 图像判别分析类型 + List> pictureDiscriminateAnalysisTypeList = + sysDictionaryItemsService.getDeviceByType("PictureDiscriminateAnalysisType"); + // 工作表中第一行是表头,不获取,从第二行开始获取 + for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { + // 获取到这一行的数据 + Row row = sheet.getRow(rowNum); + if (row == null || row.getCell(0) == null) { + continue; + } + if (StrUtil.isBlank(row.getCell(0).toString())) { + continue; + } + + SubstationDevice substationDevice = new SubstationDevice(); + // 主设备状态 + substationDevice.setDatastatus("1"); + + // 变电站编号 + String stationCode = ""; + if (row.getCell(1) != null) { + row.getCell(1).setCellType(CellType.STRING); + stationCode = row.getCell(1).getStringCellValue().trim(); + substationDevice.setStationCode(stationCode); + } + Substation substation = + substationMapper.selectOne(new LambdaQueryWrapper().eq(Substation::getStationCode + , stationCode)); + if (substation != null) { + substationDevice.setStationId(substation.getStationId()); + } + // 变电站名称 + String stationName = ""; + if (row.getCell(2) != null) { + row.getCell(2).setCellType(CellType.STRING); + stationName = row.getCell(2).getStringCellValue().trim(); + substationDevice.setStationName(stationName); + } + // 区域名称 + String areaName = ""; + if (row.getCell(3) != null) { + row.getCell(3).setCellType(CellType.STRING); + areaName = row.getCell(3).getStringCellValue().trim(); + substationDevice.setAreaName(areaName); + } + // 间隔名称 + String bayName = ""; + if (row.getCell(4) != null) { + row.getCell(4).setCellType(CellType.STRING); + bayName = row.getCell(4).getStringCellValue().trim(); + substationDevice.setBayName(bayName); + } + // 巡视点位编号 + String deviceCode = ""; + if (row.getCell(5) != null) { + row.getCell(5).setCellType(CellType.STRING); + deviceCode = row.getCell(5).getStringCellValue().trim(); + substationDevice.setDeviceCode(deviceCode); + } + + // 巡视点位名称 + String deviceName = ""; + if (row.getCell(6) != null) { + row.getCell(6).setCellType(CellType.STRING); + deviceName = row.getCell(6).getStringCellValue().trim(); + substationDevice.setDeviceName(deviceName); + } + + // 点位分类 + String deviceClass = ""; + if (row.getCell(7) != null) { + row.getCell(7).setCellType(CellType.STRING); + deviceClass = row.getCell(7).getStringCellValue().trim(); + } + deviceClass = deviceClass.trim(); + boolean isBool1 = false; + if (StrUtil.isNotBlank(deviceClass)) { + for (Map map : deviceClassList) { + String dictname = map.get("dictname").toString(); + if (deviceClass.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setDeviceClass(itemcode); + isBool1 = true; + break; + } + } + } + if (!isBool1) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":不存在该点位分类,请检查"); + } + } + + // 主设备名称 + String mainDeviceName = ""; + if (row.getCell(8) != null) { + row.getCell(8).setCellType(CellType.STRING); + mainDeviceName = row.getCell(8).getStringCellValue().trim(); + substationDevice.setMainDeviceName(mainDeviceName); + } + // (主设备类型),赋值给deviceType + String dictType = ""; + if (row.getCell(9) != null) { + row.getCell(9).setCellType(CellType.STRING); + dictType = row.getCell(9).getStringCellValue().trim(); + } + dictType = dictType.trim(); + boolean isBool = false; + log.info("主设备类型"); + if (StrUtil.isNotBlank(dictType)) { + for (Map map : equipmentType) { + String dictname = map.get("dictname").toString(); + if (dictType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setDeviceType(itemcode); + isBool = true; + break; + } + } + } + if (!isBool) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":不存在该主设备类型,请检查"); + } + } + + // (部件名称),赋值给componentName + String componentName = ""; + if (row.getCell(10) != null) { + row.getCell(10).setCellType(CellType.STRING); + componentName = row.getCell(10).getStringCellValue().trim(); + substationDevice.setComponentName(componentName); + } + + SubstationComponent substationComponent = + substationComponentMapper.selectOne(new LambdaQueryWrapper().eq(SubstationComponent::getStationCode, stationCode).eq(SubstationComponent::getStationName, stationName).eq(SubstationComponent::getAreaName, areaName).eq(SubstationComponent::getBayName, bayName).eq(SubstationComponent::getComponentName, componentName).eq(SubstationComponent::getMainDeviceName, mainDeviceName)); + if (substationComponent == null) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":数据不正确,请检查"); + } + } + substationDevice.setComponentId(substationComponent.getComponentId()); + // 区域 + substationDevice.setAreaId(substationComponent.getAreaId()); + // 间隔 + substationDevice.setBayId(substationComponent.getBayId()); + // 主设备 + substationDevice.setMainDeviceId(substationComponent.getMainDeviceId()); + + // 数据来源 + String dataType = ""; + if (row.getCell(12) != null) { + row.getCell(12).setCellType(CellType.STRING); + dataType = row.getCell(12).getStringCellValue().trim(); + } + dataType = dataType.trim(); + for (Map map : dataTypeList) { + String dictname = map.get("dictname").toString(); + if (dataType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setDataType(itemcode); + break; + } + } + + // 识别类型 + String recognitionTypeList = ""; + if (row.getCell(13) != null) { + row.getCell(13).setCellType(CellType.STRING); + recognitionTypeList = row.getCell(13).getStringCellValue().trim(); + } + + recognitionTypeList = recognitionTypeList.trim(); + if (StrUtil.isNotBlank(recognitionTypeList)) { + for (Map map : recognitionType) { + String dictname = map.get("dictname").toString(); + if (recognitionTypeList.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setRecognitionTypeList(itemcode); + if ("4".equals(itemcode)) { + substationDevice.setSaveTypeList("1"); + } else if ("5".equals(itemcode)) { + substationDevice.setSaveTypeList("3"); + } else if ("6".equals(itemcode)) { + substationDevice.setSaveTypeList("4"); + } else { + substationDevice.setSaveTypeList("2"); + } + break; + } + } + } + // 表计类型 + String meterType = ""; + if (row.getCell(14) != null) { + row.getCell(14).setCellType(CellType.STRING); + meterType = row.getCell(14).getStringCellValue().trim(); + + } + meterType = meterType.trim(); + if (StrUtil.isNotBlank(meterType)) { + for (Map map : meterTypeList) { + String dictname = map.get("dictname").toString(); + if (meterType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setMeterType(itemcode); + break; + } + } + } + JSONObject jsonObject = new JSONObject(); + // 状态分析类型 + String pictureAnalysisType = ""; + if (row.getCell(15) != null) { + row.getCell(15).setCellType(CellType.STRING); + pictureAnalysisType = row.getCell(15).getStringCellValue().trim(); + } + + pictureAnalysisType = pictureAnalysisType.trim(); + if (StrUtil.isNotBlank(pictureAnalysisType)) { + for (Map map : pictureAnalysisTypeList) { + String dictname = map.get("dictname").toString(); + if (pictureAnalysisType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + jsonObject.putOnce("PictureAnalysisType", itemcode); + break; + } + } + } + // 分析仪表特征 + String outsideFeature = ""; + if (row.getCell(16) != null) { + row.getCell(16).setCellType(CellType.STRING); + outsideFeature = row.getCell(16).getStringCellValue().trim(); + } + + outsideFeature = outsideFeature.trim(); + if (StrUtil.isNotBlank(outsideFeature)) { + for (Map map : outsideFeatureList) { + String dictname = map.get("dictname").toString(); + if (outsideFeature.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setOutsideFeature(itemcode); + break; + } + } + } + + // 缺陷分析类型 + String pictureDefectAnalysisType = ""; + if (row.getCell(17) != null) { + row.getCell(17).setCellType(CellType.STRING); + pictureDefectAnalysisType = row.getCell(17).getStringCellValue().trim(); + } + pictureDefectAnalysisType = pictureDefectAnalysisType.trim(); + if (StrUtil.isNotBlank(pictureDefectAnalysisType)) { + for (Map map : pictureDefectAnalysisTypeList) { + String dictname = map.get("dictname").toString(); + if (pictureDefectAnalysisType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + jsonObject.putOnce("PictureDefectAnalysisType", itemcode); + break; + } + } + } + // 判别分析类型 + String pictureDiscriminateAnalysisType = ""; + if (row.getCell(18) != null) { + row.getCell(18).setCellType(CellType.STRING); + pictureDiscriminateAnalysisType = row.getCell(18).getStringCellValue().trim(); + } + + pictureDiscriminateAnalysisType = pictureDiscriminateAnalysisType.trim(); + if (StrUtil.isNotBlank(pictureDiscriminateAnalysisType)) { + for (Map map : pictureDiscriminateAnalysisTypeList) { + String dictname = map.get("dictname").toString(); + if (pictureDiscriminateAnalysisType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + jsonObject.putOnce("PictureDiscriminateAnalysisType", itemcode); + break; + } + } + } + // 三种缺陷类型 + substationDevice.setPictureAnalysisTypeList(jsonObject.toString()); + // 正常范围上限 + String upperValue = ""; + if (row.getCell(19) != null) { + row.getCell(19).setCellType(CellType.STRING); + upperValue = row.getCell(19).getStringCellValue().trim(); + substationDevice.setUpperValue(upperValue); + } + + // 正常范围下限 + String lowerValue = ""; + if (row.getCell(20) != null) { + row.getCell(20).setCellType(CellType.STRING); + lowerValue = row.getCell(20).getStringCellValue().trim(); + substationDevice.setLowerValue(lowerValue); + } + // 计量单位 + String unit = ""; + if (row.getCell(21) != null) { + row.getCell(21).setCellType(CellType.STRING); + unit = row.getCell(21).getStringCellValue().trim(); + substationDevice.setUnit(unit); + } + + // 是否告警 + String isAlarm = ""; + if (row.getCell(22) != null) { + row.getCell(22).setCellType(CellType.STRING); + isAlarm = row.getCell(22).getStringCellValue().trim(); + } + if ("是".equals(isAlarm)) { + substationDevice.setIsAlarm("1"); + } else { + substationDevice.setIsAlarm("0"); + } + + // 是否识别实物ID + String identifyMaterialId = ""; + if (row.getCell(23) != null) { + row.getCell(23).setCellType(CellType.STRING); + identifyMaterialId = row.getCell(23).getStringCellValue().trim(); + } + + if ("是".equals(identifyMaterialId)) { + substationDevice.setIdentifyMaterialId("1"); + } else { + substationDevice.setIdentifyMaterialId("0"); + } + + // 备注 + String deviceInfo = ""; + if (row.getCell(24) != null) { + row.getCell(24).setCellType(CellType.STRING); + deviceInfo = row.getCell(24).getStringCellValue().trim(); + substationDevice.setPatroldeviceJson(deviceInfo); + + } + // 备注 + String phase = ""; + if (row.getCell(25) != null) { + row.getCell(25).setCellType(CellType.STRING); + phase = row.getCell(25).getStringCellValue().trim(); + } + phase = phase.trim(); + if (StrUtil.isNotBlank(phase)) { + for (Map map : devicePhaseList) { + String dictname = map.get("dictname").toString(); + if (phase.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationDevice.setPhase(itemcode); + break; + } + } + } + String dataType1 = substationDevice.getDataType(); + + if ("0x01".equals(dataType1) && JSONUtil.isTypeJSONArray(deviceInfo)) { + JSONArray jsonArray = JSONUtil.parseArray(deviceInfo); + JSONArray videoPosArray = new JSONArray(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject deviceJSON = jsonArray.getJSONObject(i); + JSONObject videoPos = new JSONObject(); + videoPos.putOnce("device_code", deviceJSON.getStr("patroldevice_code")); + videoPos.putOnce("device_pos", deviceJSON.getStr("patroldevice_pos")); + videoPos.putOnce("device_xyzf", ""); + videoPos.putOnce("robot_code", ""); + videoPos.putOnce("robot_pos", ""); + videoPos.putOnce("uav_code", ""); + videoPos.putOnce("uav_pos", ""); + videoPosArray.add(videoPos); + } + substationDevice.setVideoPos(videoPosArray.toString()); + } + int count = + this.count(new LambdaQueryWrapper().eq(SubstationDevice::getDeviceCode, + deviceCode).eq(SubstationDevice::getBayId, substationComponent.getBayId())); + if (count > 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":当前点位已经存在,请检查"); + } + } + substationDevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationDevice.setLastmodifydate(LocalDateTime.now()); + boolean ok = this.save(substationDevice); + if (!ok) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + ":导入失败,请检查"); + } + } + log.info("导入行数:" + rowNum); + + } + return ""; + } catch (IOException e) { + return e.getMessage(); + } + } + + @Override + public List getComponentTree(String bayId, String deviceName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationDevice::getBayId, bayId); + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationDevice::getDeviceName, deviceName); + } + queryWrapper.select(SubstationDevice::getBayId, + SubstationDevice::getBayName, SubstationDevice::getMainDeviceId, + SubstationDevice::getMainDeviceName, SubstationDevice::getComponentId, + SubstationDevice::getComponentName); + List> list = this.listMaps(queryWrapper); + Map bayNodeMap = new HashMap<>(); + Map mainDeviceMap = new HashMap<>(); + Map componentMap = new HashMap<>(); + List> mapList = list.stream().distinct().collect(Collectors.toList()); + for (Map map : mapList) { + String bayid = (String) map.get("bayId"); + String mainDeviceId = (String) map.get("mainDeviceId"); + String componentId = (String) map.get("componentId"); + TreeNode bayNode = bayNodeMap.computeIfAbsent(bayid, k -> new TreeNode(bayid, (String) map.get("bayName"))); + TreeNode mainDeviceNode = mainDeviceMap.computeIfAbsent(mainDeviceId, k -> new TreeNode(mainDeviceId, + (String) map.get("mainDeviceName"))); + long count = + bayNode.getChildren().stream().filter(b -> b.getName().equals(mainDeviceNode.getName())).count(); + if (count <= 0) { + bayNode.getChildren().add(mainDeviceNode); + } + TreeNode componentNode = componentMap.computeIfAbsent(componentId, k -> new TreeNode(componentId, + (String) map.get("componentName"))); + mainDeviceNode.getChildren().add(componentNode); + } + TreeNode treeNode = bayNodeMap.get(bayId); + if (treeNode == null) { + return new ArrayList<>(); + } + return treeNode.getChildren(); + } + + @Override + public TreeNode getBayTree(String stationCode, String deviceName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationDevice::getStationCode, stationCode); + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like(SubstationDevice::getDeviceName, deviceName); + } + queryWrapper.select(SubstationDevice::getStationCode, + SubstationDevice::getStationName, SubstationDevice::getStationName, SubstationDevice::getAreaId, + SubstationDevice::getAreaName, SubstationDevice::getBayId, SubstationDevice::getBayName); + List> list = this.listMaps(queryWrapper); + List> mapList = list.stream().distinct().collect(Collectors.toList()); + // 分组并构建树形结构 + Map stationMap = new HashMap<>(); + Map areaNodeMap = new HashMap<>(); + Map bayNodeMap = new HashMap<>(); + // Map mainDeviceMap = new HashMap<>(); + for (Map map : mapList) { + // String code = (String) map.get("stationCode"); + String areaId = (String) map.get("areaId"); + String bayId = (String) map.get("bayId"); + // String mainDeviceid = (String) map.get("mainDeviceId"); + // TreeNode stationNode = stationMap.computeIfAbsent(code, k -> new TreeNode(code, (String) + // map.get("stationName"))); + // 创建或获取Area节点 + TreeNode areaNode = areaNodeMap.computeIfAbsent(areaId, k -> new TreeNode(areaId, (String) + map.get("areaName"))); + // stationNode.getChildren().add(areaNode); + // 创建或获取Bay节点,并将其添加到Area节点的children中 + TreeNode bayNode = bayNodeMap.computeIfAbsent(bayId, k -> new TreeNode(bayId, (String) map + .get("bayName"))); + areaNode.getChildren().add(bayNode); + + // TreeNode mainDeviceNode = mainDeviceMap.computeIfAbsent(mainDeviceid, k -> new TreeNode + // (mainDeviceid, + // (String) map.get("mainDeviceName"))); + // bayNode.getChildren().add(mainDeviceNode); + + } + TreeNode stationNode = new TreeNode(); + List substations = + substationMapper.selectList(new LambdaQueryWrapper().eq(Substation::getStationCode, + stationCode)); + if (substations.size() <= 0) { + return null; + } + Substation substation = substations.get(0); + stationNode.setId(stationCode); + stationNode.setName(substation.getStationName()); + stationNode.setChildren(new ArrayList<>(areaNodeMap.values())); + stationMap.put(stationCode, stationNode); + return stationMap.get(stationCode); + } + + @Override + public List> getDeviceByJson(String internationalId) { + return substationDeviceMapper.getDeviceByJson(internationalId); + } + + @Override + public String selectNextUavDeviceCode() { + return substationDeviceMapper.selectNextUavDeviceCode(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationMaindeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationMaindeviceServiceImpl.java new file mode 100644 index 0000000..c151a98 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationMaindeviceServiceImpl.java @@ -0,0 +1,715 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.mapper.*; +import com.yfd.platform.modules.basedata.service.ISubstationMaindeviceService; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.system.service.ISysDictionaryService; +import com.yfd.platform.utils.SecurityUtils; +import freemarker.template.utility.NullArgumentException; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 变电站_主设备 服务实现类 + *

+ * + * + * @since 2023-03-28 + */ +@Transactional +@Service +public class SubstationMaindeviceServiceImpl extends ServiceImpl implements ISubstationMaindeviceService { + + @Resource + private SubstationComponentMapper substationComponentMapper; + + @Resource + private SubstationBayMapper substationBayMapper; + + @Resource + private SubstationMapper substationMapper; + @Resource + private SubstationAreaMapper substationAreaMapper; + + @Resource + private ISysDictionaryService sysDictionaryService; + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + /********************************** + * 用途说明: 获取主设备及部件树 + * 参数说明 name 参数名称 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getMainDeviceTree(String bayId, String name) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationMaindevice::getBayId, bayId); + if (StrUtil.isNotBlank(name)) { + queryWrapper.like(SubstationMaindevice::getMainDeviceName, name).orderByAsc(SubstationMaindevice::getCustom1); + List> list = this.listMaps(queryWrapper); + list.forEach(l -> l.put("type", "主设备")); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(SubstationComponent::getBayId, bayId).like(SubstationComponent::getComponentName, name).orderByAsc(SubstationComponent::getCustom1); + List> maps = substationComponentMapper.selectMaps(queryWrapper1); + maps.forEach(m -> m.put("type", "部件")); + list.addAll(maps); + return list; + } + queryWrapper.orderByAsc(SubstationMaindevice::getCustom1); + List> list = this.listMaps(queryWrapper); + List> listMainDevice = new ArrayList<>(); + for (Map map : list) { + map.put("type", "主设备"); + String mainDeviceId = (String) map.get("mainDeviceId"); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(SubstationComponent::getMainDeviceId, mainDeviceId); + /* if (StrUtil.isNotBlank(name)) { + queryWrapper1.like(SubstationComponent::getComponentName, name); + }*/ + queryWrapper1.orderByAsc(SubstationComponent::getCustom1); + List> list1 = substationComponentMapper.selectMaps(queryWrapper1); + map.put("count", list1.size()); + List> listComponent = new ArrayList<>(); + for (Map stringObjectMap : list1) { + stringObjectMap.put("type", "部件"); + listComponent.add(stringObjectMap); + } + if (listComponent.size() > 0) { + map.put("children", listComponent); + } + listMainDevice.add(map); + } + return listMainDevice; + } + + /********************************** + * 用途说明: 修改主设备状态 + * 参数说明 id 主设备id + * 参数说明 dataStatus 修改的状态 + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setMainDeviceStatus(String id, String dataStatus) { + SubstationMaindevice substationMaindevice = this.getById(id); + substationMaindevice.setDatastatus(dataStatus); + substationMaindevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationMaindevice.setLastmodifydate(LocalDateTime.now()); + // 修改部件状态 + boolean ok = this.updateById(substationMaindevice); + if ("0".equals(dataStatus)) { + // 获取所有部件信息 + List substationComponents = + substationComponentMapper.selectList(new LambdaQueryWrapper().eq(SubstationComponent::getMainDeviceId, id)); + for (SubstationComponent substationComponent : substationComponents) { + substationComponent.setDatastatus(dataStatus); + substationComponent.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationComponent.setLastmodifydate(LocalDateTime.now()); + substationComponentMapper.updateById(substationComponent); + } + } + return ok; + } + + /********************************** + * 用途说明: 删除主设备 + * 参数说明 id 主设备id + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteMainDevice(String id) { + SubstationMaindevice substationMaindevice = this.getById(id); + // 如果是启用状态不允许删除 + if ("1".equals(substationMaindevice.getDatastatus())) { + return false; + } + boolean ok = this.removeById(id); + String bayId = substationMaindevice.getBayId(); + List list = + list(new LambdaQueryWrapper().eq(SubstationMaindevice::getBayId, bayId).orderByAsc(SubstationMaindevice::getCustom1)); + for (int i = 0; i < list.size(); i++) { + SubstationMaindevice substationMaindevice1 = list.get(i); + substationMaindevice1.setCustom1(i + 1); + this.updateById(substationMaindevice1); + } + if (ok) { + List substationComponents = + substationComponentMapper.selectList(new LambdaQueryWrapper().eq(SubstationComponent::getMainDeviceId, id)); + for (SubstationComponent substationComponent : substationComponents) { + String componentId = substationComponent.getComponentId(); + substationComponentMapper.deleteById(componentId); + } + } + return ok; + } + + /********************************** + * 用途说明: 新增部件 + * 参数说明 substationComponent 部件对象 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean addComponent(SubstationComponent substationComponent) { + substationComponent.setDatastatus("1"); + substationComponent.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationComponent.setLastmodifydate(LocalDateTime.now()); + int insert = substationComponentMapper.insert(substationComponent); + return insert > 0; + } + + /********************************** + * 用途说明: 根据Id获取部件 + * 参数说明 componentId 部件id + * 返回值说明: com.yfd.platform.basedata.domain.SubstationComponent + ***********************************/ + @Override + public SubstationComponent getComponentById(String componentId) { + return substationComponentMapper.selectById(componentId); + } + + /********************************** + * 用途说明: 修改部件 + * 参数说明 substationComponent 部件对象 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean updateComponent(SubstationComponent substationComponent) { + substationComponent.setDatastatus("1"); + substationComponent.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationComponent.setLastmodifydate(LocalDateTime.now()); + int insert = substationComponentMapper.updateById(substationComponent); + return insert > 0; + } + + /********************************** + * 用途说明: 修改部件状态 + * 参数说明 componentId 部件id + * 参数说明 dataStatus 修改的状态 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean setComponentStatus(String componentId, String dataStatus) { + SubstationComponent substationComponent = substationComponentMapper.selectById(componentId); + if ("1".equals(dataStatus)) { + String mainDeviceId = substationComponent.getMainDeviceId(); + SubstationMaindevice substationMaindevice = this.getById(mainDeviceId); + String datastatus = substationMaindevice.getDatastatus(); + if (!dataStatus.equals(datastatus)) { + substationMaindevice.setDatastatus(dataStatus); + this.updateById(substationMaindevice); + } + } + substationComponent.setDatastatus(dataStatus); + substationComponent.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationComponent.setLastmodifydate(LocalDateTime.now()); + int i = substationComponentMapper.updateById(substationComponent); + return i > 0; + } + + /********************************** + * 用途说明: 删除部件 + * 参数说明 componentId 部件id + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteComponent(String componentId) { + SubstationComponent substationComponent = substationComponentMapper.selectById(componentId); + // 如果是启用状态不允许删除 + if ("1".equals(substationComponent.getDatastatus())) { + return false; + } + int i = substationComponentMapper.deleteById(componentId); + String mainDeviceId = substationComponent.getMainDeviceId(); + List list = + substationComponentMapper.selectList(new LambdaQueryWrapper().eq(SubstationComponent::getMainDeviceId, mainDeviceId).orderByAsc(SubstationComponent::getCustom1)); + for (int j = 0; j < list.size(); j++) { + SubstationComponent substationComponent1 = list.get(j); + substationComponent1.setCustom1(j + 1); + substationComponentMapper.updateById(substationComponent1); + } + return i > 0; + } + + /********************************** + * 用途说明: 导入文件 + * 参数说明 file + * 参数说明 type 主设备,部件 + * 返回值说明: java.lang.String + ***********************************/ + @Override + public String uploadExcel(MultipartFile file, String type) throws Exception { + if (file == null) { + throw new NullArgumentException(); + } + String filename = file.getOriginalFilename(); + if (filename == null) { + throw new NullArgumentException(); + } + String a = ""; + try { + // 调用解析文件方法 + a = parseRowCell(filename, file.getInputStream(), type); + return a; + } catch (IOException e) { + throw new Exception(e.getMessage()); + } + + } + + /********************************** + * 用途说明: 获取部件 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getComponent(String mainDeviceId, String bayId, String componentName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(mainDeviceId)) { + queryWrapper.eq(SubstationComponent::getMainDeviceId, mainDeviceId); + } + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.eq(SubstationComponent::getBayId, bayId); + } + if (StrUtil.isNotBlank(componentName)) { + queryWrapper.like(SubstationComponent::getComponentName, componentName); + } + queryWrapper.eq(SubstationComponent::getDatastatus, "1"); + queryWrapper.select(SubstationComponent::getComponentId, SubstationComponent::getComponentName, + SubstationComponent::getAreaId, SubstationComponent::getAreaName, + SubstationComponent::getMainDeviceId, SubstationComponent::getMainDeviceName, + SubstationComponent::getStationCode, SubstationComponent::getStationName, + SubstationComponent::getBayId, SubstationComponent::getBayName); + return substationComponentMapper.selectMaps(queryWrapper); + } + + /********************************** + * 用途说明: 获取当前主设备类型 + * 参数说明 deviceType + * 返回值说明: com.yfd.platform.system.domain.SysDictionaryItems + ***********************************/ + @Override + public SysDictionaryItems getMainDeviceType(String deviceType) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionary::getDictCode, "EquipmentType"); + // 获取类型 + List list = sysDictionaryService.list(queryWrapper); + if (list.size() <= 0) { + return null; + } + SysDictionary sysDictionary = list.get(0); + String id = sysDictionary.getId(); + SysDictionaryItems sysDictionaryItems = + sysDictionaryItemsService.getOne(new LambdaQueryWrapper().eq(SysDictionaryItems::getItemCode, deviceType).eq(SysDictionaryItems::getDictId, id)); + return sysDictionaryItems; + } + + /********************************** + * 用途说明: 获取主设备类型集合 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getDeviceType() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionary::getDictCode, "EquipmentType"); + // 获取类型 + List list = sysDictionaryService.list(queryWrapper); + SysDictionary sysDictionary = list.get(0); + String id = sysDictionary.getId(); + LambdaQueryWrapper eq = + new LambdaQueryWrapper().eq(SysDictionaryItems::getDictId, id).select(SysDictionaryItems::getId, SysDictionaryItems::getDictName, SysDictionaryItems::getItemCode); + List> listItems = sysDictionaryItemsService.listMaps(eq); + return listItems; + } + + /********************************** + * 用途说明: 获取间隔下的部件 + * 参数说明 bayId 间隔Id + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getComponentByBay(String bayId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(bayId)) { + queryWrapper.eq(SubstationComponent::getBayId, bayId); + } + queryWrapper.eq(SubstationComponent::getDatastatus, "1"); + queryWrapper.select(SubstationComponent::getComponentId, SubstationComponent::getComponentName); + return substationComponentMapper.selectMaps(queryWrapper); + } + + /********************************** + * 用途说明: 解析文件方法 + * 参数说明 filename 文件名 + * 参数说明 inputStream + * 返回值说明: java.lang.String + ***********************************/ + private String parseRowCell(String filename, InputStream inputStream, String type) { + try { + Workbook workbook = null; + // 判断excel的后缀,不同的后缀用不同的对象去解析 + // xls是低版本的Excel文件 + if (filename.endsWith(".xls")) { + workbook = new HSSFWorkbook(inputStream); + } + // xlsx是高版本的Excel文件 + if (filename.endsWith(".xlsx")) { + workbook = new XSSFWorkbook(inputStream); + } + if (workbook == null) { + throw new RuntimeException("上传失败"); + } + + // 取到excel 中的第一张工作表 + Sheet sheet = workbook.getSheetAt(0); + if (sheet == null) { + throw new NullArgumentException(); + } + List> equipmentType = sysDictionaryItemsService.getDeviceByType( + "EquipmentType"); + // 工作表中第一行是表头,不获取,从第二行开始获取 + for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { + // 获取到这一行的数据 + Row row = sheet.getRow(rowNum); + if (row == null || row.getCell(0) == null) { + continue; + } + if (StrUtil.isBlank(row.getCell(0).toString())) { + continue; + } + if ("01".equals(type)) { + SubstationMaindevice substationMaindevice = new SubstationMaindevice(); + // 主设备状态 + substationMaindevice.setDatastatus("1"); + + // 变电站编号 + String stationCode = ""; + if (row.getCell(1) != null) { + row.getCell(1).setCellType(CellType.STRING); + stationCode = row.getCell(1).getStringCellValue().trim(); + substationMaindevice.setStationCode(stationCode); + } + // 变电站名称 + String stationName = ""; + if (row.getCell(2) != null) { + row.getCell(2).setCellType(CellType.STRING); + stationName = row.getCell(2).getStringCellValue().trim(); + substationMaindevice.setStationName(stationName); + } + // 区域名称 + String areaName = ""; + if (row.getCell(3) != null) { + row.getCell(3).setCellType(CellType.STRING); + areaName = row.getCell(3).getStringCellValue().trim(); + substationMaindevice.setAreaName(areaName); + } + // 间隔名称 + String bayName = ""; + if (row.getCell(4) != null) { + row.getCell(4).setCellType(CellType.STRING); + bayName = row.getCell(4).getStringCellValue().trim(); + substationMaindevice.setBayName(bayName); + } + // 主设备名称 + String mainDeviceName = ""; + if (row.getCell(5) != null) { + row.getCell(5).setCellType(CellType.STRING); + mainDeviceName = row.getCell(5).getStringCellValue().trim(); + substationMaindevice.setMainDeviceName(mainDeviceName); + } + // (主设备类型),赋值给deviceType + String dictType = ""; + if (row.getCell(6) != null) { + row.getCell(6).setCellType(CellType.STRING); + dictType = row.getCell(6).getStringCellValue().trim(); + } + dictType = dictType.trim(); + boolean isBool = false; + if (StrUtil.isNotBlank(dictType)) { + + for (Map map : equipmentType) { + String dictname = map.get("dictname").toString(); + if (dictType.equals(dictname)) { + String itemcode = map.get("itemcode").toString(); + substationMaindevice.setDeviceType(itemcode); + isBool = true; + break; + } + } + } + if (!isBool) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":不存在该主设备类型,请检查"); + } + } + // 实物ID + String materialId = ""; + if (row.getCell(7) != null) { + row.getCell(7).setCellType(CellType.STRING); + materialId = row.getCell(7).getStringCellValue().trim(); + substationMaindevice.setMaterialId(materialId); + } + // 间隔id + LambdaQueryWrapper bayQueryWrapper = new LambdaQueryWrapper<>(); + bayQueryWrapper.eq(SubstationBay::getStationCode, stationCode).eq(SubstationBay::getStationName, + stationName).eq(SubstationBay::getAreaName, + areaName).eq(SubstationBay::getBayName, bayName); + List bayList = substationBayMapper.selectList(bayQueryWrapper); + if (bayList == null || bayList.size() == 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":数据不正确,请检查"); + } + } + SubstationBay substationBay = bayList.get(0); + String areaId = substationBay.getAreaId(); + substationMaindevice.setAreaId(areaId); + String bayId = substationBay.getBayId(); + substationMaindevice.setBayId(bayId); + int count = + this.count(new LambdaQueryWrapper().eq(SubstationMaindevice::getMainDeviceName, mainDeviceName)); + if (count > 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":当前主设备已经存在,请检查"); + } + } + int count1 = + this.count(new LambdaQueryWrapper().eq(SubstationMaindevice::getBayId, bayId)) + 1; + substationMaindevice.setCustom1(count1); + substationMaindevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationMaindevice.setLastmodifydate(LocalDateTime.now()); + boolean ok = this.save(substationMaindevice); + if (!ok) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + ":导入失败,请检查"); + } + } + } else { + SubstationComponent substationComponent = new SubstationComponent(); + // 部件状态 + substationComponent.setDatastatus("1"); + // 变电站编号 + String stationCode = ""; + if (row.getCell(1) != null) { + row.getCell(1).setCellType(CellType.STRING); + stationCode = row.getCell(1).getStringCellValue().trim(); + substationComponent.setStationCode(stationCode); + } + // 变电站名称 + String stationName = ""; + if (row.getCell(2) != null) { + row.getCell(2).setCellType(CellType.STRING); + stationName = row.getCell(2).getStringCellValue().trim(); + substationComponent.setStationName(stationName); + } + // 区域名称 + String areaName = ""; + if (row.getCell(3) != null) { + row.getCell(3).setCellType(CellType.STRING); + areaName = row.getCell(3).getStringCellValue().trim(); + substationComponent.setAreaName(areaName); + } + // 间隔名称 + String bayName = ""; + if (row.getCell(4) != null) { + row.getCell(4).setCellType(CellType.STRING); + bayName = row.getCell(4).getStringCellValue().trim(); + substationComponent.setBayName(bayName); + } + // 主设备名称 + String mainDeviceName = ""; + if (row.getCell(5) != null) { + row.getCell(5).setCellType(CellType.STRING); + mainDeviceName = row.getCell(5).getStringCellValue().trim(); + substationComponent.setMainDeviceName(mainDeviceName); + } + // (部件名称),赋值给componentName + String componentName = ""; + if (row.getCell(6) != null) { + row.getCell(6).setCellType(CellType.STRING); + componentName = row.getCell(6).getStringCellValue().trim(); + substationComponent.setComponentName(componentName); + } + + // 实物ID + String materialId = ""; + if (row.getCell(7) != null) { + row.getCell(7).setCellType(CellType.STRING); + materialId = row.getCell(7).getStringCellValue().trim(); + substationComponent.setMaterialId(materialId); + } + List list1 = + this.list(new LambdaQueryWrapper().eq(SubstationMaindevice::getStationCode, stationCode).eq(SubstationMaindevice::getStationName, stationName).eq(SubstationMaindevice::getAreaName, areaName).eq(SubstationMaindevice::getBayName, bayName).eq(SubstationMaindevice::getMainDeviceName, mainDeviceName)); + if (list1 == null || list1.size() == 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":数据不正确,请检查"); + } + } + SubstationMaindevice substationMaindevice = list1.get(0); + substationComponent.setAreaId(substationMaindevice.getAreaId()); + substationComponent.setBayId(substationMaindevice.getBayId()); + substationComponent.setMainDeviceId(substationMaindevice.getMainDeviceId()); + // 主设备类型 + substationComponent.setDeviceType(substationMaindevice.getDeviceType()); + // 主设备id + substationComponent.setMainDeviceId(substationMaindevice.getMainDeviceId()); + int count = + substationComponentMapper.selectCount(new LambdaQueryWrapper().eq(SubstationComponent::getMainDeviceId, substationMaindevice.getMainDeviceId()).eq(SubstationComponent::getComponentName, componentName)); + if (count > 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + ":当前部件已经存在,请检查"); + } + } + int count1 = + substationComponentMapper.selectCount(new LambdaQueryWrapper().eq(SubstationComponent::getBayId, substationMaindevice.getMainDeviceId())) + 1; + substationComponent.setCustom1(count1); + // 最近修改人 + substationComponent.setLastmodifier(SecurityUtils.getCurrentUsername()); + // 修改时间 + substationComponent.setLastmodifydate(LocalDateTime.now()); + int insert = substationComponentMapper.insert(substationComponent); + if (insert <= 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + ":新增失败,请检查"); + } + } + } + + } + return ""; + } catch (IOException e) { + return e.getMessage(); + } + } + + /********************************** + * 用途说明: 获取主设备导航树 + * 参数说明 stationCode + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getSubstationMainDeviceTree(String stationCode, String mainDeviceName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationMaindevice::getStationCode, stationCode).eq(SubstationMaindevice::getDatastatus, "1"); + if (StrUtil.isNotBlank(mainDeviceName)) { + queryWrapper.like(SubstationMaindevice::getMainDeviceName, mainDeviceName); + } + List list = this.list(queryWrapper); + Map> collect = + list.stream().collect(Collectors.groupingBy(SubstationMaindevice::getAreaName)); + List> mainDeviceTree = new ArrayList<>(); + for (String areaName : collect.keySet()) { + Map map = new HashMap<>(); + map.put("name", areaName); + List substationMaindevices = collect.get(areaName); + Map> collect1 = + substationMaindevices.stream().collect(Collectors.groupingBy(SubstationMaindevice::getBayName)); + List> areaTree = new ArrayList<>(); + for (String bayName : collect1.keySet()) { + Map bayMap = new HashMap<>(); + bayMap.put("name", bayName); + List substationMaindevices1 = collect1.get(bayName); + bayMap.put("children", substationMaindevices1); + areaTree.add(bayMap); + } + map.put("children", areaTree); + mainDeviceTree.add(map); + } + return mainDeviceTree; + } + + @Override + public TreeNode getSubstationMainTree(String stationCode, String mainDeviceName) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationMaindevice::getStationCode, stationCode); + if (StrUtil.isNotBlank(mainDeviceName)) { + queryWrapper.like(SubstationMaindevice::getMainDeviceName, mainDeviceName); + } + queryWrapper.select(SubstationMaindevice::getStationCode, + SubstationMaindevice::getStationName, SubstationMaindevice::getStationName, + SubstationMaindevice::getAreaId, + SubstationMaindevice::getAreaName, SubstationMaindevice::getBayId, SubstationMaindevice::getBayName); + List> list = this.listMaps(queryWrapper); + List> mapList = list.stream().distinct().collect(Collectors.toList()); + // 分组并构建树形结构 + Map stationMap = new HashMap<>(); + Map areaNodeMap = new HashMap<>(); + Map bayNodeMap = new HashMap<>(); + Map mainDeviceMap = new HashMap<>(); + for (Map map : mapList) { + // String code = (String) map.get("stationCode"); + String areaId = (String) map.get("areaId"); + String bayId = (String) map.get("bayId"); + String mainDeviceId = (String) map.get("mainDeviceId"); + // String mainDeviceid = (String) map.get("mainDeviceId"); + // TreeNode stationNode = stationMap.computeIfAbsent(code, k -> new TreeNode(code, (String) + // map.get("stationName"))); + // 创建或获取Area节点 + TreeNode areaNode = areaNodeMap.computeIfAbsent(areaId, k -> new TreeNode(areaId, (String) + map.get("areaName"))); + // stationNode.getChildren().add(areaNode); + // 创建或获取Bay节点,并将其添加到Area节点的children中 + TreeNode bayNode = bayNodeMap.computeIfAbsent(bayId, k -> new TreeNode(bayId, (String) map + .get("bayName"))); + areaNode.getChildren().add(bayNode); + + TreeNode mainDeviceNode = mainDeviceMap.computeIfAbsent(mainDeviceId, k -> new TreeNode + (mainDeviceId, + (String) map.get("mainDeviceName"))); + bayNode.getChildren().add(mainDeviceNode); + + } + TreeNode stationNode = new TreeNode(); + List substations = + substationMapper.selectList(new LambdaQueryWrapper().eq(Substation::getStationCode, + stationCode)); + if (substations.size() <= 0) { + return null; + } + Substation substation = substations.get(0); + stationNode.setId(stationCode); + stationNode.setName(substation.getStationName()); + stationNode.setChildren(new ArrayList<>(areaNodeMap.values())); + stationMap.put(stationCode, stationNode); + return stationMap.get(stationCode); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationModelServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationModelServiceImpl.java new file mode 100644 index 0000000..8bf6595 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationModelServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import com.yfd.platform.modules.basedata.domain.SubstationModel; +import com.yfd.platform.modules.basedata.mapper.SubstationModelMapper; +import com.yfd.platform.modules.basedata.service.ISubstationModelService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 变电站-三维动态模型 服务实现类 + *

+ * + * @author zhengsl + * @since 2024-04-05 + */ +@Service +public class SubstationModelServiceImpl extends ServiceImpl implements ISubstationModelService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationPatroldeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationPatroldeviceServiceImpl.java new file mode 100644 index 0000000..e3ad714 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationPatroldeviceServiceImpl.java @@ -0,0 +1,667 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.BetweenFormatter; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.DynamicTask; +import com.yfd.platform.config.redis.RiisConstants; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.DeviceChannelMapper; +import com.yfd.platform.modules.basedata.mapper.SubstationMapper; +import com.yfd.platform.modules.basedata.mapper.SubstationPatroldeviceMapper; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.system.domain.RolePatroldevice; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.RolePatroldeviceMapper; +import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SecurityUtils; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 变电站_巡视设备 服务实现类 + *

+ * + * @since 2023-03-28 + */ +@Service +@Transactional +public class SubstationPatroldeviceServiceImpl extends ServiceImpl implements ISubstationPatroldeviceService { + + @Resource + private SysUserMapper sysUserMapper; + + @Resource + private RolePatroldeviceMapper rolePatroldeviceMapper; + + @Resource + private SubstationMapper substationMapper; + + @Resource + private DynamicTask dynamicTask; + + @Resource + private DeviceChannelMapper deviceChannelMapper; + + @Resource + private RedisTemplate redisTemplate; + + @Resource + private SubstationPatroldeviceMapper substationPatroldeviceMapper; + + + @Resource + private HttpRESTfulUtils httpUtil; + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + /********************************** + * 用途说明: 删除巡视设备 + * 参数说明 id 巡视设备id + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteSubstationDeviceById(String id) { + String[] split = id.split(","); + for (String s : split) { + SubstationPatroldevice substationPatroldevice = this.getById(s); + if ("1".equals(substationPatroldevice.getDatastatus())) { + continue; + } + String patroldeviceCode = substationPatroldevice.getPatroldeviceCode(); + this.remove(new LambdaQueryWrapper().eq(SubstationPatroldevice::getRobotsCode, patroldeviceCode)); + rolePatroldeviceMapper.delete(new LambdaQueryWrapper().eq(RolePatroldevice::getDeviceid, s)); + this.removeById(s); + } + return true; + } + + /********************************** + * 用途说明: + * 参数说明 id 巡视设备id + * 参数说明 dataStatus 设备状态 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean setSubstationDeviceStatus(String id, String dataStatus) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + //根据id 修改是否启用静默任务 ,最近修改人,最近修改时间 + updateWrapper.eq(SubstationPatroldevice::getPatroldeviceId, id).set(SubstationPatroldevice::getDatastatus, + dataStatus).set(SubstationPatroldevice::getLastmodifier, SecurityUtils.getCurrentUsername()).set(SubstationPatroldevice::getLastmodifydate, new Timestamp(System.currentTimeMillis())); + return this.update(updateWrapper); + } + + /********************************** + * 用途说明: 根据用户id查询所有摄像头id + * 参数说明 id 用户id + * 返回值说明: java.util.List + ***********************************/ + @Override + public List getCameraIds(String id) { + List roleIds = sysUserMapper.getRoleid(id); + List cameraIds = new ArrayList<>(); + for (String roleId : roleIds) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(RolePatroldevice::getRoleid, roleId).select(RolePatroldevice::getDeviceid); + List list = rolePatroldeviceMapper.selectList(queryWrapper); + List collect = list.stream().map(RolePatroldevice::getDeviceid).collect(Collectors.toList()); + cameraIds.addAll(collect); + } + return cameraIds; + } + + /********************************** + * 用途说明: 新增或修改机器人 + * 参数说明 substationPatroldevices + * 返回值说明: void + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public void addOrUpdatePatrolRobot(List substationPatroldevices) { + String currentUsername = SecurityUtils.getCurrentUsername(); + LocalDateTime now = LocalDateTime.now(); + // 如果id为空则为新增,否则为修改 + for (SubstationPatroldevice substationPatroldevice : substationPatroldevices) { + if (BeanUtil.isEmpty(substationPatroldevice)) { + continue; + } + // 根据变电站id获取名称和编码 + String stationId = substationPatroldevice.getStationId(); + if (StrUtil.isNotBlank(stationId)) { + Substation substation = substationMapper.selectById(stationId); + substationPatroldevice.setStationName(substation.getStationName()); + substationPatroldevice.setStationCode(substation.getStationCode()); + } + substationPatroldevice.setLastmodifier(currentUsername); + substationPatroldevice.setLastmodifydate(now); + substationPatroldevice.setDatastatus("1"); + String patroldeviceId = substationPatroldevice.getPatroldeviceId(); + if (StrUtil.isBlank(patroldeviceId)) { + this.save(substationPatroldevice); + } else { + this.updateById(substationPatroldevice); + } + } + } + + /********************************** + * 用途说明: 对摄像头添加静默监视任务 + * 参数说明 + * 返回值说明: 无 + ***********************************/ + @Override + public void addDailyMonitorTask() { + List devices = this.list( + new LambdaQueryWrapper() + .in(SubstationPatroldevice::getType, "1", "2", "3", "10") + .eq(SubstationPatroldevice::getDatastatus, "1") + .eq(SubstationPatroldevice::getDailymonitor, "1") + .orderByAsc(SubstationPatroldevice::getStationCode, SubstationPatroldevice::getPatroldeviceCode) + ); + for (SubstationPatroldevice device : devices) { + startPatroldeviceJob(device); + } + } + + //启动单个摄像机静默监视任务 + private void startPatroldeviceJob(SubstationPatroldevice device) { + + String key = "DailyMonitorJob_" + device.getStationCode() + "_" + device.getPatroldeviceCode(); + String slienceParams = device.getSlienceParams(); + String channelinfo = device.getChannelinfo(); + String patroldevice_code = ""; + String patrolchannel_code = ""; + String rtspurl = ""; + if (ObjUtil.isNotEmpty(channelinfo) && JSONUtil.isTypeJSONArray(channelinfo)) { + JSONArray channels = JSONUtil.parseArray(channelinfo); + for (int i = 0; i < channels.size(); i++) { + JSONObject channel = channels.getJSONObject(i); + if (channel.getStr("channel_type").equals("1")) {//1=可见光 + if (channel.getStr("from_device").equals("1")) {//1=摄像机 + patroldevice_code = device.getInternationalId(); + patrolchannel_code = channel.getStr("channel_code"); + } else {//1=来源录像机 + patroldevice_code = channel.getStr("recorder_code"); + patrolchannel_code = channel.getStr("recorder_channelcode"); + } + rtspurl = channel.getStr("channel_rtspurl"); + break; + } + } + } + + if (StrUtil.isNotEmpty(slienceParams)) { + JSONArray jsonArray = JSONUtil.parseArray(slienceParams); + for (Object o : jsonArray) { + JSONObject deviceobj = new JSONObject(); + JSONObject jsonObject = JSONUtil.parseObj(o); + deviceobj.putOnce("station_code", device.getStationCode()); + deviceobj.putOnce("patroldevice_id", device.getPatroldeviceId()); + deviceobj.putOnce("patroldevice_name", device.getPatroldeviceName()); + deviceobj.putOnce("patroldevice_code", patroldevice_code); + deviceobj.putOnce("patrolchannel_code", patrolchannel_code); + deviceobj.putOnce("area_name", device.getAreaName()); + deviceobj.putOnce("place", device.getPlace()); + deviceobj.putOnce("rtspurl", rtspurl); + if (jsonObject.getStr("type").equals("wcgz") || jsonObject.getStr("type").equals("wcaqm")) { + deviceobj.putOnce("sliencetype", "ryxw"); + } else { + deviceobj.putOnce("sliencetype", jsonObject.getStr("type")); + } + + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId, "fedeb076ef96422eee01c88d7f7efd4e").eq(SysDictionaryItems::getItemCode, deviceobj.get("sliencetype")); //缺陷类别字典 + queryWrapper.select(SysDictionaryItems::getItemCode, + SysDictionaryItems::getDictName, SysDictionaryItems::getCustom1, + SysDictionaryItems::getCustom2); + SysDictionaryItems item = sysDictionaryItemsService.getOne(queryWrapper); + JSONArray typeresult = new JSONArray(); + if (StrUtil.isNotEmpty(item.getCustom1()) && JSONUtil.isTypeJSONArray(item.getCustom1())) { + //确认一下是否是这个字段 + typeresult.addAll(JSONUtil.parseArray(item.getCustom1())); + } + jsonObject.putOpt("defectresulttypes", JSONUtil.toJsonStr(typeresult)); + + deviceobj.putOnce("customParams", jsonObject); + key += "_" + jsonObject.getStr("type"); + int taskcycle = 30000; + if (StrUtil.isNotEmpty(jsonObject.getStr("frame_interval"))) { + taskcycle = (int) Math.ceil(Integer.parseInt(jsonObject.getStr("frame_interval")) * 1000); + } + //调用静默监视任务 + if (taskcycle > 0) { + dynamicTask.startCron(key, new DailyMonitorJob(JSONUtil.toJsonStr(deviceobj)), taskcycle); + } + } + } + } + + private void stopPatrolDeviceJob(SubstationPatroldevice device) { + String key = "DailyMonitorJob_" + device.getStationCode() + "_" + device.getPatroldeviceCode(); + String sliencetypes = device.getSlienceType(); + if (StrUtil.isNotEmpty(sliencetypes)) { + String[] sliencetype = sliencetypes.split(","); + for (String s : sliencetype) { + key += "_" + s; + try { + dynamicTask.stop(key); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + } + } + + /** + * 判断摄像机是否处于巡视任务状态 + * + * @param stationcode 变电站编号 + * @param devicecode 摄像机编号 + * @return String 是否工作中 + */ + @Override + public String isWorkingofPatroldevice(String stationcode, String devicecode) { + List list = this.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, devicecode)); + String code = devicecode; + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + String channelinfo = substationPatroldevice.getChannelinfo(); + if (JSONUtil.isTypeJSONArray(channelinfo)) { + JSONArray jarray = JSONUtil.parseArray(channelinfo); + for (int i = 0; i < jarray.size(); i++) { + JSONObject jobj = jarray.getJSONObject(i); + if (jobj.getStr("channel_type").equals("2")) { //红外 + if (ObjUtil.isNotEmpty(jobj.getStr("pan_code"))) { + code = jobj.getStr("pan_code"); + } + } + } + } + } + String rediskey = RiisConstants.Patroldevice_IsWorking + stationcode + "_" + code; + String isworking = redisTemplate.opsForValue().get(rediskey); + if (StrUtil.isEmpty(isworking)) { + return "0"; + } + return isworking; + } + + /********************************** + * 用途说明: 摄像机录像完整率统计 + * 参数说明 page + * 参数说明 stationId + * 参数说明 patrolDeviceName + * 参数说明 channel + * 参数说明 flag + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getCameraIntegrityRate(Page> page, String stationId, + String patrolDeviceName, String channel, String flag) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationId)) { + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + } + if (StrUtil.isNotBlank(patrolDeviceName)) { + queryWrapper.eq(SubstationPatroldevice::getPatroldeviceName, patrolDeviceName); + } + if (StrUtil.isNotBlank(channel)) { + + } + if (StrUtil.isNotBlank(flag)) { + + } + Page> mapPage = this.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + DateTime date = DateUtil.date(); + records.forEach(r -> { + Date startDate = DateUtil.parse("2023-05-20 02:00:00"); + //相差毫秒数 + long millis = DateUtil.between(startDate, date, DateUnit.MS); + String formatSecond = DateUtil.formatBetween(millis, BetweenFormatter.Level.SECOND); + // 录像时长 + r.put("duration", formatSecond); + // 录像周期 + r.put("cycle", formatSecond); + int num = (int) (((float) millis / millis) * 100 + 0.5); + // 录像完整率 + r.put("rate", num); + if (num == 100) { + r.put("isComplete", "1"); + } else { + r.put("isComplete", "0"); + } + }); + return mapPage; + } + + /********************************** + * 用途说明: 设置静默监视是否启用 + * 参数说明 + * 返回值说明: void + ***********************************/ + @Override + public boolean setDailyMonitorStatus(String id, String dailyMonitor) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + //根据id 修改是否启用静默任务 ,最近修改人,最近修改时间 + updateWrapper.eq(SubstationPatroldevice::getPatroldeviceId, id).set(SubstationPatroldevice::getDailymonitor, + dailyMonitor).set(SubstationPatroldevice::getLastmodifier, SecurityUtils.getCurrentUsername()).set(SubstationPatroldevice::getLastmodifydate, new Timestamp(System.currentTimeMillis())); + this.update(updateWrapper); + SubstationPatroldevice device = this.getById(id); + //启动或者停止对应静默任务号 + if ("1".equals(dailyMonitor)) { + startPatroldeviceJob(device); + } else { + stopPatrolDeviceJob(device); + } + return true; + } + + @Override + public Page> getCameraStat(Page> page, String stationId, + String patrolDeviceName, String channel, String flag) { + + Page> cameraStatPage = substationPatroldeviceMapper.getCameraStat(page, stationId, + patrolDeviceName, channel, flag); + List> records = cameraStatPage.getRecords(); + records.forEach(r -> { + if (ObjectUtil.isNotEmpty(r.get("ipaddr"))) { + DeviceChannel deviceChannel = this.queryChannelByDeviceIp(r.get("ipaddr").toString()); + if (deviceChannel != null) { + String cameraStatByCode = this.getCameraStatByCode(deviceChannel.getChannelid()); + if (StrUtil.isNotBlank(cameraStatByCode)) { + long sum = 90 * 24 * 60; + long duration = Long.parseLong(cameraStatByCode) / 60; + r.put("duration", duration); + r.put("rate", (int) (((float) duration / sum) * 100 + 0.5)); + r.put("cycle", 90); + } + } + } + }); + cameraStatPage.setRecords(records); + return cameraStatPage; + } + + @Override + public List> getCameraStat(String stationCode) { + List> cameraStatReport = substationPatroldeviceMapper.getCameraStatReport(stationCode); + cameraStatReport.forEach(r -> { + String channelId = r.get("channelId").toString(); + String cameraStatByCode = this.getCameraStatByCode(channelId); + if (StrUtil.isNotBlank(cameraStatByCode)) { + long sum = 90 * 24 * 60; + long duration = Long.parseLong(cameraStatByCode) / 60; + r.put("duration", duration); +// String num = String.format("%.3f", ((float) duration * 100 / sum)); + r.put("rate", String.format("%.3f", ((float) duration * 100 / sum))); + r.put("cycle", 90); + } + }); + return cameraStatReport; + } + + /********************************** + * 用途说明: 单个摄像机完整率 + * 参数说明 channelId + * 返回值说明: java.util.Map + ***********************************/ + @Override + public String getCameraStatByCode(String channelId) { + String redisKey = Constant.REDIS_RECORD_TIMES_PRE + channelId; + if (ObjectUtil.isEmpty(redisTemplate.opsForValue().get(redisKey))) { + return null; + } + return String.valueOf(redisTemplate.opsForValue().get(redisKey)); + } + + /********************************** + * 用途说明: 巡视设备累计运行天数 + * 参数说明 page + * 参数说明 stationId + * 参数说明 patrolDeviceName + * 参数说明 patrolDeviceCode + * 参数说明 type + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getRobotOnlineRate(Page> page, String stationId, + String patrolDeviceName, String patrolDeviceCode, String type) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationId)) { + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + } + if (StrUtil.isNotBlank(patrolDeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patrolDeviceName); + } + if (StrUtil.isNotBlank(patrolDeviceCode)) { + queryWrapper.eq(SubstationPatroldevice::getPatroldeviceCode, patrolDeviceCode); + } + if (StrUtil.isNotBlank(type)) { + queryWrapper.eq(SubstationPatroldevice::getType, type); + } else { + queryWrapper.in(SubstationPatroldevice::getType, "1", "2", "3", "13"); + } + queryWrapper.select(SubstationPatroldevice::getPatroldeviceId, SubstationPatroldevice::getPatroldeviceCode, + SubstationPatroldevice::getPatroldeviceName, SubstationPatroldevice::getType, + SubstationPatroldevice::getOnline).orderByAsc(SubstationPatroldevice::getPatroldeviceCode); + Page> mapPage = this.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + records.forEach(r -> { + // 最近开始时间 + r.put("recentStartTime", "2023-08-01 00:00:00"); + // 最近结束间 + r.put("recentEndTime", "2023-08-08 00:00:00"); + // 在线时长 + r.put("onlineDuration", 168); + // 累计离线次数 + r.put("onlineNumber", 20); + // 连续正常运行天数 + r.put("normalDay", 2); + // 自检正常天数 + r.put("checkNormal", 8); + }); + mapPage.setRecords(records); + return mapPage; + } + + /********************************** + * 用途说明: 巡视设备累计运行天数 + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public List> getRobotOnlineRate() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SubstationPatroldevice::getType, "1", "2", "3", "13"); + queryWrapper.select(SubstationPatroldevice::getPatroldeviceId, SubstationPatroldevice::getPatroldeviceCode, + SubstationPatroldevice::getPatroldeviceName, SubstationPatroldevice::getType, + SubstationPatroldevice::getOnline).orderByAsc(SubstationPatroldevice::getPatroldeviceCode); + List> mapList = this.listMaps(queryWrapper); + mapList.forEach(r -> { + // 最近开始时间 + r.put("recentStartTime", "2023-08-01 00:00:00"); + // 最近结束间 + r.put("recentEndTime", "2023-08-08 00:00:00"); + // 在线时长 + r.put("onlineDuration", 168); + // 累计离线次数 + r.put("onlineNumber", 20); + // 连续正常运行天数 + r.put("normalDay", 2); + // 自检正常天数 + r.put("checkNormal", 8); + }); + return mapList; + } + + /********************************** + * 用途说明: 新增巡视设备 + * 参数说明 patrolDeviceInfo + * 返回值说明: boolean + ***********************************/ + @Override + public boolean addPatrolDevice(String patrolDeviceInfo) { + JSONObject jsonObject = JSONUtil.parseObj(patrolDeviceInfo); + SubstationPatroldevice substationPatroldevice = JSONUtil.toBean(jsonObject, SubstationPatroldevice.class); + String stationId = substationPatroldevice.getStationId(); + int count = + this.count(new LambdaQueryWrapper().eq(SubstationPatroldevice::getStationId, + stationId).eq(SubstationPatroldevice::getPatroldeviceCode, + substationPatroldevice.getPatroldeviceCode())); + if (count > 0) { + return false; + } + String id = IdUtil.fastSimpleUUID(); + substationPatroldevice.setPatroldeviceId(id); + substationPatroldevice.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationPatroldevice.setLastmodifydate(LocalDateTime.now()); + substationPatroldevice.setDatastatus("1"); + return this.save(substationPatroldevice); + } + + @Override + public boolean setEffectiveArea(String type, String id, String effecttivearea) { + //todo 设置有限区域 + return true; + } + + /********************************** + * 用途说明: 导出巡视设备台账 + * 参数说明 stationId 变电站id + * 参数说明 type 设备类型 + * 参数说明 patrolDeviceName 设备名称 + * 参数说明 deviceModel 设备型号 + * 参数说明 manufacturer 生产厂家 + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void exportPatrolDeviceList(String stationId, String type, String patrolDeviceName, String deviceModel, + String manufacturer, HttpServletResponse response) throws IOException { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationId)) { + queryWrapper.eq(SubstationPatroldevice::getStationId, stationId); + } + if (StrUtil.isNotBlank(type)) { + if ("10".equals(type)) { + queryWrapper.in(SubstationPatroldevice::getType, "10", "11"); + } else { + queryWrapper.eq(SubstationPatroldevice::getType, type); + } + } + if (StrUtil.isNotBlank(patrolDeviceName)) { + queryWrapper.like(SubstationPatroldevice::getPatroldeviceName, patrolDeviceName); + } + if (StrUtil.isNotBlank(deviceModel)) { + queryWrapper.like(SubstationPatroldevice::getDeviceModel, deviceModel); + } + if (StrUtil.isNotBlank(manufacturer)) { + queryWrapper.like(SubstationPatroldevice::getManufacturer, manufacturer); + } + List list = this.list(queryWrapper); + List> mapList = new ArrayList<>(); + List> patrolEquipmentType = sysDictionaryItemsService.getDeviceByType( + "PatrolEquipmentType"); + + for (SubstationPatroldevice substationPatroldevice : list) { + String devtype = ""; + for (Map map : patrolEquipmentType) { + String itemcode = (String) map.get("itemcode"); + if (itemcode.equals(type)) { + devtype = map.get("dictname").toString(); + break; + } + } + Map map = new LinkedHashMap<>(); + map.put("devtype", devtype); + map.put("devname", substationPatroldevice.getPatroldeviceName()); + map.put("devdesc", ""); + map.put("devmodel", substationPatroldevice.getDeviceModel()); + map.put("manufacturer", substationPatroldevice.getManufacturer()); + map.put("madein", ""); + map.put("productionnum", substationPatroldevice.getProductionCode()); + map.put("productiondate", substationPatroldevice.getProductionDate()); + map.put("phyassetID", substationPatroldevice.getMaterialId()); + map.put("position_x", ""); + map.put("position_y", ""); + map.put("region", ""); + map.put("cabinet", ""); + map.put("hwRev", ""); + map.put("SwRev", ""); + if ("1".equals(type) || "2".equals(type) || "3".equals(type)) { + map.put("SwRevCRC", ""); + map.put("IPAddr", substationPatroldevice.getIpaddr()); + map.put("MacAddr", substationPatroldevice.getMacaddr()); + } else if ("10".equals(type)) { + map.put("SwRevCRC", ""); + map.put("IPAddr", substationPatroldevice.getIpaddr()); + map.put("MacAddr", substationPatroldevice.getMacaddr()); + } else if ("11".equals(type)) { + map.put("SwRevCRC", ""); + map.put("IPAddr", substationPatroldevice.getIpaddr()); + map.put("MacAddr", substationPatroldevice.getMacaddr()); + } else if ("13".equals(type)) { + + } + mapList.add(map); + } + // String[] headers = {"设备类型", "设备名称", "中文描述", "设备型号", "生产厂家", "生产国家", "出厂编号", "出厂日期", "实物 ID", + // "相对空间位置 x", "相对空间位置 y", "所属区域标识"}; + // String[] keys = { "devtype", "devname", "devdesc", "devmodel","manufacturer", "madein", + // "productionnum", "productiondate", "phyassetID","position_x", "position_y", "region"}; + // FileUtil.excelImg(mapList, headers, keys, "", "", response); + FileUtil.downloadExcel(mapList, response); + } + + @Override + public DeviceChannel queryChannelByDeviceIp(String ipaddr) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DeviceChannel::getAddress, ipaddr); + List deviceChannels = deviceChannelMapper.selectList(queryWrapper); + if (deviceChannels == null || deviceChannels.size() == 0) { + return null; + } + return deviceChannels.get(0); + } + + @Override + public List> getDockInfoBySn(String dockSn) { + + return substationPatroldeviceMapper.getDockInfoBySn(dockSn); + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationServiceImpl.java new file mode 100644 index 0000000..420c00e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/SubstationServiceImpl.java @@ -0,0 +1,991 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.mapper.*; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.StringUtils; +import freemarker.template.utility.NullArgumentException; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 变电站 服务实现类 + *

+ * + * + * @since 2023-03-28 + */ +@Service +@Transactional +public class SubstationServiceImpl extends ServiceImpl implements ISubstationService { + + @Resource + private SubstationMapper substationMapper; + + @Resource + private SubstationAreaMapper substationAreaMapper; + + @Resource + private SubstationBayMapper substationBayMapper; + + @Resource + private IUserService userService; + + @Resource + private SubstationMaindeviceMapper substationMaindeviceMapper; + + @Resource + private SubstationDeviceMapper substationDeviceMapper; + /** + * 用户头像图片路径 + */ + @Value("${file-space.system}") + private String systempath; + + /********************************** + * 用途说明: 获取变电站区域间隔导航树 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getSubstationTree(String stationId) { + List> stationList = new ArrayList<>(); + Substation substation = this.substationMapper.selectById(stationId); + List> substationBays = substationBayMapper.getSubstationBayInfo(stationId); + Map>> collect = + substationBays.stream().collect(Collectors.groupingBy(s -> s.get("areaName").toString())); + Map stationMap = new HashMap<>(); + stationMap.put("name", substation.getStationName()); + List> treeList = new ArrayList<>(); + for (String areaName : collect.keySet()) { + Map areaMap = new HashMap<>(); + areaMap.put("name", areaName); + List> substationBays1 = collect.get(areaName); + areaMap.put("children", substationBays1); + treeList.add(areaMap); + } + stationMap.put("children", treeList); + stationList.add(stationMap); + return stationList; + } + // @Override + // public List> getSubstationTree(String stationId) { + // Substation substation = this.substationMapper.selectById(stationId); + // List substationAreas = + // substationAreaMapper.selectList(new LambdaQueryWrapper().eq + // (SubstationArea::getDatastatus, "1").eq(SubstationArea::getStationCode, substation + // .getStationCode()).orderByAsc(SubstationArea::getCustom1)); + // List> stationList = new ArrayList<>(); + // Map stationMap = new HashMap<>(); + // stationMap.put("name", substation.getStationName()); + // List> treeList = new ArrayList<>(); + // for (SubstationArea substationArea : substationAreas) { + // Map areaMap = new HashMap<>(); + // List substationBays = + // substationBayMapper.selectList(new LambdaQueryWrapper().eq + // (SubstationBay::getDatastatus, "1").eq(SubstationBay::getAreaId, substationArea.getAreaId() + // ).orderByAsc(SubstationBay::getCustom1)); + // if (substationBays == null || substationBays.size() <= 0) { + // continue; + // } + // areaMap.put("name", substationArea.getAreaName()); + // List> bayList = new ArrayList<>(); + // for (SubstationBay substationBay : substationBays) { + // Map bayMap = new HashMap<>(); + // Integer count = + // substationMaindeviceMapper.selectCount(new LambdaQueryWrapper() + // .eq(SubstationMaindevice::getBayId, substationBay.getBayId())); + // if (count == null) { + // count = 0; + // } + // Integer deviceCount = + // substationDeviceMapper.selectCount(new LambdaQueryWrapper().eq + // (SubstationDevice::getBayId, substationBay.getBayId())); + // if (deviceCount == null) { + // deviceCount = 0; + // } + // bayMap.put("bayId", substationBay.getBayId()); + // bayMap.put("name", substationBay.getBayName()); + // bayMap.put("deviceCount", deviceCount); + // bayMap.put("count", count); + // bayMap.put("stationCode", substationBay.getStationCode()); + // bayMap.put("stationName", substationBay.getStationName()); + // bayMap.put("areaId", substationBay.getAreaId()); + // bayMap.put("areaName", substationBay.getAreaName()); + // bayMap.put("orderNo", substationBay.getCustom1()); + // bayMap.put("stationId", substation.getStationId()); + // bayList.add(bayMap); + // } + // areaMap.put("children", bayList); + // treeList.add(areaMap); + // } + // stationMap.put("children", treeList); + // stationList.add(stationMap); + // return stationList; + // } + + /********************************** + * 用途说明: 获取变电站区域导航树 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getSubstationAreaTree(String stationId) { + Substation substation = this.substationMapper.selectById(stationId); + List list = + substationAreaMapper.selectList(new LambdaQueryWrapper().eq(SubstationArea::getDatastatus, "1").eq(SubstationArea::getStationCode, substation.getStationCode()).orderByAsc(SubstationArea::getCustom1)); + return buildTreeArea(list); + } + + + /********************************** + * 用途说明: 获取区域下所有间隔 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getSubstationBayByArea(String stationCode, String areaId, String bayName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.eq(SubstationBay::getStationCode, stationCode); + } + if (StrUtil.isNotBlank(bayName)) { + queryWrapper.like(SubstationBay::getBayName, bayName); + } + if (StrUtil.isNotBlank(areaId)) { + queryWrapper.eq(SubstationBay::getAreaId, areaId); + } + queryWrapper.eq(SubstationBay::getDatastatus, "1").orderByAsc(SubstationBay::getCustom1); + List> list = substationBayMapper.selectMaps(queryWrapper); + return list; + } + + /********************************** + * 用途说明: 修改变电站 + * 参数说明 substation + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateSubstation(Substation substation) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + String stationId = substation.getStationId(); + String stationCode = substation.getStationCode(); + queryWrapper.ne(Substation::getStationId, stationId).eq(Substation::getStationCode, stationCode); + int count = this.count(queryWrapper); + if (count > 0) { + throw new RuntimeException("当前变电站已经存在"); + } + // LambdaQueryWrapper areaQueryWrapper = new LambdaQueryWrapper<>(); + // areaQueryWrapper.eq(SubstationArea::getStationCode, stationCode); + // int count1 = substationAreaMapper.selectCount(areaQueryWrapper); + // if (count1 > 0) { + // throw new RuntimeException("当前变电站有区域关联不能修改"); + // } + // int count2 = + // substationPatroldeviceMapper.selectCount(new LambdaQueryWrapper().eq(SubstationPatroldevice::getStationId, stationId)); + // if (count2 > 0) { + // throw new RuntimeException("当前变电站有巡视设备关联不能修改"); + // } + String username = userService.getUsername(); + substation.setLastmodifier(username); + substation.setLastmodifydate(LocalDateTime.now()); + return this.updateById(substation); + + } + + @Override + public int getStationOnline(String stationcode) { + int online = 1; + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Substation::getStationCode, stationcode).eq(Substation::getIsStationFlag, "1").eq(Substation::getOnline, "0"); + int count = substationMapper.selectCount(queryWrapper); + if (count > 0) { + online = 0;//边缘节点不在线 + } + return online; + } + + /********************************** + * 用途说明: 获取变电站树 + * 参数说明 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getSubstationBayTree(String name, Set orgCodeByUser) { + SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (usertype != 0) { + if (orgCodeByUser == null || orgCodeByUser.size() == 0) { + return null; + } else { + queryWrapper.in(Substation::getStationCode, orgCodeByUser); + } + } + // 如果是带参数的就不返回树 + if (StrUtil.isNotBlank(name)) { + queryWrapper.like(Substation::getStationName, name).orderByAsc(Substation::getCustom1); + List> list = this.listMaps(queryWrapper); + list.forEach(l -> { + if (ObjectUtil.isNotEmpty(l.get("contactPhone"))) { + String contactPhone = l.get("contactPhone").toString(); + l.put("contactPhone", DesensitizedUtil.mobilePhone(contactPhone)); + } + }); + list.forEach(m -> m.put("type", "变电站")); + List> allMap = new ArrayList<>(list); + LambdaQueryWrapper areaQueryWrapper = new LambdaQueryWrapper<>(); + areaQueryWrapper.like(SubstationArea::getAreaName, name).orderByAsc(SubstationArea::getCustom1); + List> list1 = substationAreaMapper.selectMaps(areaQueryWrapper); + list1.forEach(m -> m.put("type", "区域")); + allMap.addAll(list1); + LambdaQueryWrapper bayQueryWrapper = new LambdaQueryWrapper<>(); + bayQueryWrapper.like(SubstationBay::getBayName, name).orderByAsc(SubstationBay::getCustom1); + List> list2 = substationBayMapper.selectMaps(bayQueryWrapper); + list2.forEach(m -> m.put("type", "间隔")); + allMap.addAll(list2); + return allMap; + } + queryWrapper.orderByAsc(Substation::getCustom1); + List> list = this.listMaps(queryWrapper); + list.forEach(l -> { + if (ObjectUtil.isNotEmpty(l.get("contactPhone"))) { + String contactPhone = l.get("contactPhone").toString(); + l.put("contactPhone", DesensitizedUtil.mobilePhone(contactPhone)); + } + }); + List> listMap = new ArrayList<>(); + for (Map map : list) { + String stationCode = (String) map.get("stationCode"); + LambdaQueryWrapper areaQueryWrapper = new LambdaQueryWrapper<>(); + // 关联区间 + areaQueryWrapper.eq(SubstationArea::getStationCode, stationCode); + areaQueryWrapper.orderByAsc(SubstationArea::getCustom1); + List> areaList = substationAreaMapper.selectMaps(areaQueryWrapper); + List> listAreaMap = new ArrayList<>(); + for (Map areaMap : areaList) { + String areaId = (String) areaMap.get("areaId"); + LambdaQueryWrapper bayQueryWrapper = new LambdaQueryWrapper<>(); + // 关联间隔 + bayQueryWrapper.eq(SubstationBay::getAreaId, areaId); + bayQueryWrapper.orderByAsc(SubstationBay::getCustom1); + List> list1 = substationBayMapper.selectMaps(bayQueryWrapper); + List> listBay = new ArrayList<>(); + for (Map stringObjectMap : list1) { + stringObjectMap.put("type", "间隔"); + listBay.add(stringObjectMap); + } + if (listBay.size() > 0) { + areaMap.put("children", listBay); + } + areaMap.put("type", "区域"); + listAreaMap.add(areaMap); + } + if (listAreaMap.size() > 0) { + map.put("children", listAreaMap); + } + map.put("type", "变电站"); + listMap.add(map); + } + return listMap; + } + + /*********************************** + * 用途说明:上传图片 + * 参数说明 + * image 图片 + * 返回值说明: 是否上传成功 + ***********************************/ + @Override + public String uploadIcon(MultipartFile image) { + // 图片存储地址 + String imgPath = systempath + "station"; + String iconname = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(image.getOriginalFilename()); + //上传图标并获取图标名称 (图片改为png格式) + return Objects.requireNonNull(FileUtil.upload(image, imgPath, iconname)).getName(); + } + + /********************************** + * 用途说明: 删除图片 + * 参数说明 id id + * 参数说明 filename 图片名称 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteImage(String id, String filename) { + // 图片存储地址 + String imgPath = systempath + "station" + File.separator + filename; + if (StrUtil.isBlank(id)) { + FileUtil.del(imgPath); + return true; + } + Substation substation = this.getById(id); + String images = substation.getImages(); + if (StrUtil.isBlank(images)) { + return true; + } + String[] split = images.split(","); + List stringList = new ArrayList<>(split.length); + stringList.addAll(Arrays.asList(split)); + stringList.remove(filename); + String img = StringUtils.join(stringList, ","); + substation.setImages(img); + boolean ok = this.updateById(substation); + if (ok) { + FileUtil.del(imgPath); + return true; + } else { + return false; + } + } + + @Override + public boolean cancelImage(String id, String images) { + // 图片存储地址 + String imgPath = systempath + "station" + File.separator; + if (StrUtil.isBlank(images)) { + return true; + } + String[] split = images.split(","); + if (StrUtil.isBlank(id)) { + for (String img : split) { + String filePath = imgPath + img; + FileUtil.del(filePath); + } + return true; + } + Substation substation = this.getById(id); + String image = substation.getImages(); + String[] split1 = image.split(","); + List strImg = new ArrayList<>(Arrays.asList(split1)); + for (String img : split) { + if (strImg.contains(img)) { + continue; + } + String filePath = imgPath + img; + FileUtil.del(filePath); + } + return true; + } + + /********************************** + * 用途说明: 删除变电站 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean deleteSubstation(String stationId) { + Substation substation = substationMapper.selectById(stationId); + String stationCode = substation.getStationCode(); + int i = substationMapper.deleteById(stationId); + if (i <= 0) { + return false; + } + List list = this.list(new LambdaQueryWrapper().orderByAsc(Substation::getCustom1)); + for (int i1 = 0; i1 < list.size(); i1++) { + Substation substation1 = list.get(i1); + substation1.setCustom1(i1 + 1); + this.updateById(substation1); + } + List substationAreas = + substationAreaMapper.selectList(new LambdaQueryWrapper().eq(SubstationArea::getStationCode, + stationCode)); + for (SubstationArea substationArea : substationAreas) { + String areaId = substationArea.getAreaId(); + this.deleteSubstationArea(areaId); + } + return true; + } + + /********************************** + * 用途说明: 删除区域 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean deleteSubstationArea(String areaId) { + int i = substationAreaMapper.deleteById(areaId); + if (i <= 0) { + return false; + } + List substationAreas = + substationAreaMapper.selectList(new LambdaQueryWrapper().orderByAsc(SubstationArea::getCustom1)); + for (int i1 = 0; i1 < substationAreas.size(); i1++) { + SubstationArea substationArea = substationAreas.get(i1); + substationArea.setCustom1(i1 + 1); + substationAreaMapper.updateById(substationArea); + } + List substationBays = + substationBayMapper.selectList(new LambdaQueryWrapper().eq(SubstationBay::getAreaId, + areaId)); + for (SubstationBay substationBay : substationBays) { + String bayId = substationBay.getBayId(); + this.deleteSubstationBay(bayId); + } + return true; + } + + /********************************** + * 用途说明: 删除间隔 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteSubstationBay(String bayId) { + int i = substationBayMapper.deleteById(bayId); + List substationBays = + substationBayMapper.selectList(new LambdaQueryWrapper().orderByAsc(SubstationBay::getCustom1)); + for (int i1 = 0; i1 < substationBays.size(); i1++) { + SubstationBay substationBay = substationBays.get(i1); + substationBay.setCustom1(i1 + 1); + substationBayMapper.updateById(substationBay); + } + return i > 0; + } + + /********************************** + * 用途说明: 修改变电站状态 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setSubstationStatus(String stationId, String dataStatus) { + Substation substation = substationMapper.selectById(stationId); + substation.setDatastatus(dataStatus); + substation.setLastmodifier(SecurityUtils.getCurrentUsername()); + substation.setLastmodifydate(LocalDateTime.now()); + int i = substationMapper.updateById(substation); + if ("0".equals(dataStatus)) { + List substationAreas = + substationAreaMapper.selectList(new LambdaQueryWrapper().eq(SubstationArea::getStationCode, substation.getStationCode())); + for (SubstationArea substationArea : substationAreas) { + String areaId = substationArea.getAreaId(); + this.setSubstationAreaStatus(areaId, dataStatus); + } + } + return i > 0; + } + + /********************************** + * 用途说明: 修改区域状态 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setSubstationAreaStatus(String areaId, String dataStatus) { + SubstationArea substationArea = substationAreaMapper.selectById(areaId); + substationArea.setDatastatus(dataStatus); + substationArea.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationArea.setLastmodifydate(LocalDateTime.now()); + int i = substationAreaMapper.updateById(substationArea); + if ("0".equals(dataStatus)) { + List substationBays = + substationBayMapper.selectList(new LambdaQueryWrapper().eq(SubstationBay::getAreaId, areaId)); + for (SubstationBay substationBay : substationBays) { + String bayId = substationBay.getBayId(); + this.setSubstationBayStatus(bayId, dataStatus); + } + } else { + String stationCode = substationArea.getStationCode(); + Substation one = this.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, + stationCode)); + String datastatus = one.getDatastatus(); + if (!dataStatus.equals(datastatus)) { + one.setDatastatus(dataStatus); + this.updateById(one); + } + } + return i > 0; + } + + /********************************** + * 用途说明: 修改间隔状态 + * 参数说明 stationId + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setSubstationBayStatus(String bayId, String dataStatus) { + SubstationBay substationBay = substationBayMapper.selectById(bayId); + String areaId = substationBay.getAreaId(); + // 如果间隔开启而区域状态未开启在,则将区域开启 + if ("1".equals(dataStatus)) { + SubstationArea substationArea = substationAreaMapper.selectById(areaId); + String datastatus = substationArea.getDatastatus(); + if (!dataStatus.equals(datastatus)) { + substationArea.setDatastatus(dataStatus); + substationAreaMapper.updateById(substationArea); + } + String stationCode = substationArea.getStationCode(); + List list = this.list(new LambdaQueryWrapper().eq(Substation::getStationCode, + stationCode)); + if (list != null && list.size() > 0) { + Substation substation = list.get(0); + String datastatus1 = substation.getDatastatus(); + if (!dataStatus.equals(datastatus1)) { + substation.setDatastatus(dataStatus); + this.updateById(substation); + } + } + } + substationBay.setDatastatus(dataStatus); + substationBay.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationBay.setLastmodifydate(LocalDateTime.now()); + int i = substationBayMapper.updateById(substationBay); + return i > 0; + } + + /********************************** + * 用途说明: 导入文件 + * 参数说明 file + * 返回值说明: java.lang.String + ***********************************/ + @Override + public String uploadExcel(MultipartFile file, String type) throws Exception { + if (file == null) { + throw new NullArgumentException(); + } + String filename = file.getOriginalFilename(); + if (filename == null) { + throw new NullArgumentException(); + } + String a = ""; + try { + // 调用解析文件方法 + a = parseRowCell(filename, file.getInputStream(), type); + return a; + } catch (IOException e) { + throw new Exception(e.getMessage()); + } + + } + + /********************************** + * 用途说明: 解析文件方法 + * 参数说明 filename 文件名 + * 参数说明 inputStream + * 返回值说明: java.lang.String + ***********************************/ + @Transactional(rollbackFor = Exception.class) + public String parseRowCell(String filename, InputStream inputStream, String type) { + try { + Workbook workbook = null; + // 判断excel的后缀,不同的后缀用不同的对象去解析 + // xls是低版本的Excel文件 + if (filename.endsWith(".xls")) { + workbook = new HSSFWorkbook(inputStream); + } + // xlsx是高版本的Excel文件 + if (filename.endsWith(".xlsx")) { + workbook = new XSSFWorkbook(inputStream); + } + if (workbook == null) { + throw new RuntimeException("上传失败"); + } + + // 取到excel 中的第一张工作表 + Sheet sheet = workbook.getSheetAt(0); + if (sheet == null) { + throw new NullArgumentException(); + } + // 工作表中第一行是表头,不获取,从第二行开始获取 + for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) { + // 获取到这一行的数据 + Row row = sheet.getRow(rowNum); + if (row == null || row.getCell(0) == null) { + continue; + } + if (StrUtil.isBlank(row.getCell(0).toString())) { + continue; + } + importExcel(row, type); + } + return ""; + } catch (IOException e) { + return e.getMessage(); + } + } + + private void importExcel(Row row, String type) { + if ("01".equals(type)) { + Substation substation = new Substation(); + substation.setDatastatus("1"); + // 变电站编号 + String stationCode = ""; + if (row.getCell(1) != null) { + row.getCell(1).setCellType(CellType.STRING); + stationCode = row.getCell(1).getStringCellValue().trim(); + substation.setStationCode(stationCode); + } + // 变电站名称 + String stationName = ""; + if (row.getCell(2) != null) { + row.getCell(2).setCellType(CellType.STRING); + stationName = row.getCell(2).getStringCellValue().trim(); + substation.setStationName(stationName); + } + // 变电站IP + String stationIp = ""; + if (row.getCell(4) != null) { + row.getCell(4).setCellType(CellType.STRING); + stationIp = row.getCell(4).getStringCellValue().trim(); + substation.setStationIp(stationIp); + } + // 变电站节点ID + String stationId = ""; + if (row.getCell(5) != null) { + row.getCell(5).setCellType(CellType.STRING); + stationId = row.getCell(5).getStringCellValue().trim(); + substation.setStationId(stationId); + } + // 电压等级 + String voltLevel = ""; + if (row.getCell(6) != null) { + row.getCell(6).setCellType(CellType.STRING); + voltLevel = row.getCell(6).getStringCellValue().trim(); + substation.setVoltLevel(voltLevel); + } + // 所属省份 + String provinceName = ""; + if (row.getCell(7) != null) { + row.getCell(7).setCellType(CellType.STRING); + provinceName = row.getCell(7).getStringCellValue().trim(); + substation.setProvinceName(provinceName); + } + // 所属地市 + String cityName = ""; + if (row.getCell(8) != null) { + row.getCell(8).setCellType(CellType.STRING); + cityName = row.getCell(8).getStringCellValue().trim(); + substation.setCityName(cityName); + } + // 公司名称 + String companyName = ""; + if (row.getCell(9) != null) { + row.getCell(9).setCellType(CellType.STRING); + companyName = row.getCell(9).getStringCellValue().trim(); + substation.setCompanyName(companyName); + } + // 联系电话 + String contactPhone = ""; + if (row.getCell(10) != null) { + row.getCell(10).setCellType(CellType.STRING); + contactPhone = row.getCell(10).getStringCellValue().trim(); + substation.setContactPhone(contactPhone); + } + // 联系人 + String contactPerson = ""; + if (row.getCell(11) != null) { + row.getCell(11).setCellType(CellType.STRING); + contactPerson = row.getCell(11).getStringCellValue().trim(); + substation.setContactPerson(contactPerson); + } + // 变电站地址 + String stationAddress = ""; + if (row.getCell(12) != null) { + row.getCell(12).setCellType(CellType.STRING); + stationAddress = row.getCell(12).getStringCellValue().trim(); + substation.setStationAddress(stationAddress); + } + // 是否边缘节点 + String isStationFlag = ""; + if (row.getCell(13) != null) { + row.getCell(13).setCellType(CellType.STRING); + isStationFlag = row.getCell(13).getStringCellValue().trim(); + substation.setIsStationFlag(isStationFlag); + } + // 变电站介绍 + String introduction = ""; + if (row.getCell(14) != null) { + row.getCell(14).setCellType(CellType.STRING); + introduction = row.getCell(14).getStringCellValue().trim(); + substation.setIntroduction(introduction); + } + int count1 = this.count() + 1; + substation.setCustom1(count1); + substation.setLastmodifier(SecurityUtils.getCurrentUsername()); + substation.setLastmodifydate(LocalDateTime.now()); + boolean ok = this.save(substation); + if (!ok) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "数据不正确,请检查"); + } + } + } else if ("02".equals(type)) { + SubstationArea substationArea = new SubstationArea(); + substationArea.setDatastatus("1"); + // 取到这一行的第一列数据 (变电站编号),赋值给stationCode + String stationCode = ""; + if (row.getCell(1) != null) { + row.getCell(1).setCellType(CellType.STRING); + stationCode = row.getCell(1).getStringCellValue().trim(); + substationArea.setStationCode(stationCode); + } + + // 变电站名称 + String stationName = ""; + if (row.getCell(2) != null) { + row.getCell(2).setCellType(CellType.STRING); + stationName = row.getCell(2).getStringCellValue().trim(); + substationArea.setStationName(stationName); + } + + Integer count2 = + substationMapper.selectCount(new LambdaQueryWrapper().eq(Substation::getStationCode, + stationCode).eq(Substation::getStationName, stationName)); + if (count2 <= 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "不存在该变电站,请检查"); + } + } + // 区域名称 + String areaName = ""; + if (row.getCell(3) != null) { + row.getCell(3).setCellType(CellType.STRING); + areaName = row.getCell(3).getStringCellValue().trim(); + substationArea.setAreaName(areaName); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationArea::getStationCode, stationCode).eq(SubstationArea::getAreaName, areaName); + int count = substationAreaMapper.selectCount(queryWrapper); + if (count > 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "区域已存在,请检查"); + } + } + int count1 = + substationAreaMapper.selectCount(new LambdaQueryWrapper().eq(SubstationArea::getStationCode, stationCode).orderByAsc(SubstationArea::getCustom1)) + 1; + substationArea.setCustom1(count1); + substationArea.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationArea.setLastmodifydate(LocalDateTime.now()); + int insert = substationAreaMapper.insert(substationArea); + if (insert <= 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "数据不正确,请检查"); + } + } + } else { + SubstationBay substationBay = new SubstationBay(); + substationBay.setDatastatus("1"); + + // 变电站编号 + String stationCode = ""; + if (row.getCell(1) != null) { + row.getCell(1).setCellType(CellType.STRING); + stationCode = row.getCell(1).getStringCellValue().trim(); + substationBay.setStationCode(stationCode); + } + + // 变电站名称 + String stationName = ""; + if (row.getCell(2) != null) { + row.getCell(2).setCellType(CellType.STRING); + stationName = row.getCell(2).getStringCellValue().trim(); + substationBay.setStationName(stationName); + } + + // Integer count2 = + // substationMapper.selectCount(new LambdaQueryWrapper().eq + // (Substation::getStationCode, + // stationCode).eq(Substation::getStationName, stationName)); + // if (count2 <= 0) { + // if (row.getCell(0) != null) { + // row.getCell(0).setCellType(CellType.STRING); + // throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + + // "不存在该变电站,请检查"); + // } + // } + + // 区域名称 + String areaName = ""; + if (row.getCell(3) != null) { + row.getCell(3).setCellType(CellType.STRING); + areaName = row.getCell(3).getStringCellValue().trim(); + substationBay.setAreaName(areaName); + } + + // (间隔名称),赋值给bayName + String bayName = ""; + if (row.getCell(4) != null) { + row.getCell(4).setCellType(CellType.STRING); + bayName = row.getCell(4).getStringCellValue().trim(); + substationBay.setBayName(bayName); + } + // 获取区域Id + List substationAreas = + substationAreaMapper.selectList(new LambdaQueryWrapper().eq(SubstationArea::getStationCode, stationCode).eq(SubstationArea::getStationName, stationName).eq(SubstationArea::getAreaName, areaName)); + if (substationAreas == null || substationAreas.size() == 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "数据不正确,请检查"); + } + } + SubstationArea substationArea = substationAreas.get(0); + int count1 = + substationBayMapper.selectCount(new LambdaQueryWrapper().eq(SubstationBay::getAreaId, + substationArea.getAreaId()).orderByAsc(SubstationBay::getCustom1)) + 1; + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationBay::getAreaName, substationArea.getAreaName()).eq(SubstationBay::getBayName, + bayName); + int count = substationBayMapper.selectCount(queryWrapper); + if (count > 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "数据不正确,请检查"); + } + } + substationBay.setCustom1(count1); + substationBay.setAreaId(substationArea.getAreaId()); + substationBay.setLastmodifier(SecurityUtils.getCurrentUsername()); + substationBay.setLastmodifydate(LocalDateTime.now()); + int insert = substationBayMapper.insert(substationBay); + if (insert <= 0) { + if (row.getCell(0) != null) { + row.getCell(0).setCellType(CellType.STRING); + throw new RuntimeException("序号" + row.getCell(0).getStringCellValue().trim() + "数据不正确,请检查"); + } + } + } + } + + /********************************** + * 用途说明: 构造树结构 + * 参数说明 list + * 返回值说明: java.util.List> + ***********************************/ + private List> buildTree(List list) { + // 按照变电站名称分组 + Map> collect = + list.stream().collect(Collectors.groupingBy(SubstationBay::getStationCode)); + List> maps = new ArrayList<>(); + // 循环查询结构树 + for (String key : collect.keySet()) { + Map map = new HashMap<>(); + List list1 = this.list(new LambdaQueryWrapper().eq(Substation::getStationCode, + key)); + if (list1 == null || list1.size() == 0) { + continue; + } + Substation substation = list1.get(0); + map.put("name", substation.getStationName()); + List substationBays = collect.get(key); + Map> areaCollect = + substationBays.stream().collect(Collectors.groupingBy(SubstationBay::getAreaName)); + List> maps1 = new ArrayList<>(); + for (String areaKey : areaCollect.keySet()) { + Map areaMap = new HashMap<>(); + areaMap.put("name", areaKey); + List substationBays1 = areaCollect.get(areaKey); + List> bayMap = new ArrayList<>(); + for (SubstationBay substationBay : substationBays1) { + Map bayCollect = new HashMap<>(); + bayCollect.put("bayId", substationBay.getBayId()); + Integer count = + substationMaindeviceMapper.selectCount(new LambdaQueryWrapper().eq(SubstationMaindevice::getBayId, substationBay.getBayId())); + if (count == null) { + count = 0; + } + Integer deviceCount = + substationDeviceMapper.selectCount(new LambdaQueryWrapper().eq(SubstationDevice::getBayId, substationBay.getBayId())); + if (deviceCount == null) { + deviceCount = 0; + } + bayCollect.put("deviceCount", deviceCount); + bayCollect.put("count", count); + bayCollect.put("name", substationBay.getBayName()); + bayCollect.put("stationCode", substationBay.getStationCode()); + bayCollect.put("stationName", substationBay.getStationName()); + bayCollect.put("areaId", substationBay.getAreaId()); + bayCollect.put("areaName", substationBay.getAreaName()); + bayCollect.put("orderNo", substationBay.getCustom1()); + bayCollect.put("stationId", substation.getStationId()); + bayMap.add(bayCollect); + } + if (bayMap.size() > 0) { + bayMap.sort(Comparator.comparing(r -> r.get("custom1").toString())); + areaMap.put("children", bayMap); + } + maps1.add(areaMap); + } + // maps1.sort(Comparator.comparing(r -> r.get("custom1").toString())); + map.put("children", maps1); + maps.add(map); + } + return maps; + } + + /********************************** + * 用途说明: 构造区域树结构 + * 参数说明 list + * 返回值说明: java.util.List> + ***********************************/ + private List> buildTreeArea(List list) { + // 按照变电站名称分组 + Map> collect = + list.stream().collect(Collectors.groupingBy(SubstationArea::getStationCode)); + List> maps = new ArrayList<>(); + // 循环查询结构树 + for (String key : collect.keySet()) { + List substations = + substationMapper.selectList(new LambdaQueryWrapper().eq(Substation::getStationCode, + key)); + Substation substation = substations.get(0); + Map map = new HashMap<>(); + map.put("name", substation.getStationName()); + map.put("stationCode", substation.getStationCode()); + map.put("stationId", substation.getStationId()); + List substationAreas = collect.get(key); + List> maps1 = new ArrayList<>(); + for (SubstationArea substationArea : substationAreas) { + Map areaMap = new HashMap<>(); + areaMap.put("name", substationArea.getAreaName()); + areaMap.put("stationCode", substationArea.getStationCode()); + areaMap.put("stationName", substationArea.getStationName()); + areaMap.put("stationId", substation.getStationId()); + areaMap.put("areaId", substationArea.getAreaId()); + maps1.add(areaMap); + } + map.put("children", maps1); + maps.add(map); + } + return maps; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/WeatherLogServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/WeatherLogServiceImpl.java new file mode 100644 index 0000000..01e4126 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/basedata/service/impl/WeatherLogServiceImpl.java @@ -0,0 +1,69 @@ +package com.yfd.platform.modules.basedata.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.WeatherLog; +import com.yfd.platform.modules.basedata.mapper.WeatherLogMapper; +import com.yfd.platform.modules.basedata.service.IWeatherLogService; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 微气象数据日志表 服务实现类 + *

+ * + * + * @since 2023-04-27 + */ +@Service +public class WeatherLogServiceImpl extends ServiceImpl implements IWeatherLogService { + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + /********************************** + * 用途说明: 查询环境信息 + * 参数说明 stationId + * 返回值说明: java.util.Map + ***********************************/ + @Override + public Map getWeatherLogList(String stationId) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WeatherLog::getStationId, stationId).eq(WeatherLog::getDatastatus, "1").orderByDesc(WeatherLog::getTime); + List list = this.list(queryWrapper); + Map> collect = + list.stream().filter(s -> StrUtil.isNotBlank(s.getType())).collect(Collectors.groupingBy(WeatherLog::getType)); + List> environmentType = sysDictionaryItemsService.getDeviceByType("EnvironmentType"); + Map weatherDevice = new HashMap<>(); + for (Map map : environmentType) { + String itemcode = map.get("itemcode").toString(); + String value = ""; + for (String type : collect.keySet()) { + List weatherLogs = collect.get(type); + if (type.equals(itemcode)) { + WeatherLog weatherLog = weatherLogs.get(0); + String value1 = weatherLog.getValue(); + if (StrUtil.isNotBlank(value1)) { + value = value1; + } + break; + } + } + String custom2 = map.get("custom2").toString(); + weatherDevice.put(custom2, value); + } + String environment = "气温" + weatherDevice.get("temperature") + "°C,气压" + weatherDevice.get("pressure") + "Kpa" + + ",风速" + weatherDevice.get("windSpeed") + "m/s"; + weatherDevice.put("environment", environment); + return weatherDevice; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/CameraUtil.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/CameraUtil.java new file mode 100644 index 0000000..2f1db76 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/CameraUtil.java @@ -0,0 +1,540 @@ +package com.yfd.platform.modules.deviceapi; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import cn.hutool.system.OsInfo; +import com.alibaba.fastjson.JSONObject; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.deviceapi.GeneralFunction.AddOSD; +import com.yfd.platform.modules.deviceapi.GeneralFunction.CaptureJPEGPicture; +import com.yfd.platform.modules.deviceapi.GeneralFunction.Login; +import com.yfd.platform.modules.deviceapi.Thermometry.JpegWithAppendData; +import com.yfd.platform.modules.deviceapi.Thermometry.ThreadGetOnceTempData; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SpringContextHolder; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URLDecoder; +import java.text.DecimalFormat; +import java.util.List; +import java.util.*; + +/** + * + * @Date: 2023/5/22 17:01 + * @Description: + */ + +@Slf4j +public class CameraUtil { + + public static String DLL_PATH; + + static { + String path = "E:\\project\\riis-server-java\\riis-server\\src\\main\\resources\\lib" + + "\\HCNetSDK.dll"; +// String path = "D:\\变电站项目1108\\net-dev-sdk-231109\\net-dev-sdk-231109\\win64\\HKNetDeviceSDK" + +// "\\HCNetSDK.dll"; + OsInfo osInfo = new OsInfo(); + if (osInfo.isLinux()) { + String loadLibrary = "/usr/lib64/lib/hkliblinux64/"; + path = loadLibrary + "libhcnetsdk.so"; + } + DLL_PATH = path; + } + + static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; + //用户句柄 + public static int lUserID = -1; + //人员信息状态 + // static int dwState = -1; + //下发人脸数据状态 + // static int dwFaceState = -1; + //设备字符集 + // static int iCharEncodeType = 0; + + public static void main(String[] args) { + // getTemperature("haikang", "192.168.0.21", (short) 8000, "admin", "hkdq8888", "", "", + // ""); + // getStorageStatus("haikang", "192.168.1.39", (short) 8000, "admin", "JY123456"); + getTemperature("haikang", "192.168.1.66", (short) 8000, "admin", "JY123456", null, null, null); + } + + /********************************** + * 用途说明: 调用sdk设置图片温度 + * 参数说明 requestId + * 参数说明 objectId + * 参数说明 filePath + * 参数说明 manufacturer + * 参数说明 ip + * 参数说明 port + * 参数说明 username + * 参数说明 password + * 返回值说明: com.alibaba.fastjson.JSONObject + ***********************************/ + public static JSONObject callPicAnalyse(String requestId, String objectId, String filePath, String manufacturer, + String ip, short port, String username, String password, + String outsideAngle, String csvFilePath) throws IOException { + // 获取spring bean 中 HttpServerConfig 类实例 + HttpServerConfig config = + SpringContextHolder.getBean(HttpServerConfig.class); + // 获取spring bean 中 HttpRESTfulUtils 类实例 + HttpRESTfulUtils httpUtil = + SpringContextHolder.getBean(HttpRESTfulUtils.class); + // 拼接图片路径 + String imgPath = config.getSnapFilePath() + URLDecoder.decode(filePath, "utf-8"); + String temperature = getTemperature(manufacturer, ip, port, username, password, outsideAngle, csvFilePath, + filePath); + // 温度字体 + Font font = new Font("宋体", Font.BOLD, 35); + // 温度字体颜色 + Color color = new Color(255, 255, 255); + CameraUtil.addWaterMark(imgPath, imgPath, temperature, color, font); + JSONObject jsonObject = new JSONObject(); + JSONObject jsonObject1 = new JSONObject(); + jsonObject.put("requestId", requestId); + List> mapList = new ArrayList<>(); + Map map = new HashMap<>(); + map.put("objectId", objectId); + List> mapList1 = new ArrayList<>(); + Map map1 = new HashMap<>(); + map1.put("type", "infrared"); + map1.put("value", temperature); + if (StrUtil.isBlank(temperature)) { + jsonObject1.put("code", "201"); + map1.put("code", "2001"); + } else { + jsonObject1.put("code", "200"); + map1.put("code", "2000"); + map1.put("conf", temperature); + String imageurl = String.format("http://%s:%s/snapfile?filename=%s", config.getPatrolIp(), + config.getPatrolPort(), filePath); + if (StrUtil.isNotEmpty(config.getPatrolAppname())) { + imageurl = String.format("http://%s:%s/%s/snapfile?filename=%s", config.getPatrolIp(), + config.getPatrolPort(), config.getPatrolAppname(), filePath); + } + map1.put("resImagePath", imageurl); + } + map1.put("desc", "红外温度测量"); + cn.hutool.json.JSONObject dataPos = JSONUtil.parseObj(outsideAngle); + JSONArray pos = dataPos.getJSONArray("pos"); + JSONArray posArray = new JSONArray(); + if (pos.size() == 4 && "rectangle".equals(dataPos.getStr("type"))) { + // 左下角 + cn.hutool.json.JSONObject ojb1 = pos.getJSONObject(0); + Integer x1 = ojb1.get("x", Integer.class); + Integer y1 = ojb1.get("y", Integer.class); + ojb1.putOpt("x", x1); + ojb1.putOpt("y", y1); + // 右上角 + cn.hutool.json.JSONObject ojb2 = pos.getJSONObject(2); + Integer x2 = ojb2.get("x", Integer.class); + Integer y2 = ojb2.get("y", Integer.class); + ojb2.putOpt("x", x2); + ojb2.putOpt("y", y2); + posArray.add(ojb1); + log.info(ojb1.toString()); + posArray.add(ojb2); + log.info(ojb2.toString()); + } + if (pos.size() == 1 && "point".equals(dataPos.getStr("type"))) { + // 点坐标 + cn.hutool.json.JSONObject ojb = pos.getJSONObject(0); + Integer x1 = ojb.get("x", Integer.class); + Integer y1 = ojb.get("y", Integer.class); + ojb.putOpt("x", x1); + ojb.putOpt("y", y1); + posArray.add(ojb); + } + JSONArray dataArray = new JSONArray(); + JSONObject dataJson = new JSONObject(); + dataJson.put("coors", posArray); + dataArray.add(dataJson); + map1.put("pos", dataArray); + mapList1.add(map1); + map.put("results", mapList1); + mapList.add(map); + jsonObject.put("resultList", mapList); + String api = "picAnalyseRetNotify"; + if (StrUtil.isNotBlank(config.getPatrolAppname())) { + api = "riis-system/picAnalyseRetNotify"; + } + httpUtil.sendHttpPost("json", config.getPatrolIp(), config.getPatrolPort(), "", api, + jsonObject, null); + return jsonObject1; + } + + public static String getTemperature(String manufacturer, String ip, short port, String username, String password, + String outsideAngle, String csvFilePath, String filePath) { + + // 根据生产厂家判断对应方法 + if ("dahua".equals(manufacturer)) { + // return getDaHuaTemperature(ip, port, username, password, outsideAngle); + return ""; + } else if ("yushi".equals(manufacturer)) { + //throw new RuntimeException("暂时没有 “宇视” SDK"); + return ""; + } else if ("haikang".equals(manufacturer) || "Hikvision".equals(manufacturer)) { + log.info("进入getTemperature"); + return getHikTemperature(ip, port, username, password, outsideAngle, csvFilePath, filePath); + } else if ("dali".equals(manufacturer)) { + return getDaLiTemperature(ip, port, username, password, outsideAngle, csvFilePath); + } else { + //throw new RuntimeException("暂时没有该生产厂家"); + return ""; + } + } + + + + public static String captureJPEGPicture(String manufacturer, String ip, short port, String username, + String password, int iChannelNum, String imagefilename) { + + // 根据生产厂家判断对应方法 + if ("dahua".equals(manufacturer)) { + return "暂时没有 “大华” SDK"; + } else if ("yushi".equals(manufacturer)) { + return "暂时没有 “宇视” SDK"; + } else if ("haikang".equals(manufacturer)) { + return HikCaptureJPEGPicture(ip, port, username, password, iChannelNum, imagefilename); + } else { + return "暂时没有该生产厂家"; + } + } + + /********************************** + * 用途说明: 海康摄像机抓图 + * 参数说明 ip 摄像机ip + * 参数说明 port 摄像机端口 + * 参数说明 username 用户名 + * 参数说明 password 密码 + * 参数说明 iChannelNum 通道号 + * 返回值说明: java.lang.String + ***********************************/ + public static String HikCaptureJPEGPicture(String ip, short port, String username, String password, + int iChannelNum,String imagefilename) { + /*初始SDK*/ + hCNetSDK.NET_DVR_Init(); + // hCNetSDK.NET_DVR_SetLogToFile(3, "D:/JavaDemoLog", true); + //TODO 库加载代码 设置异常消息回调 报警分离 + + //登陆 + Login login = new Login(); + lUserID = login.getlUserID(ip, port, username, password); + if (lUserID == -1) { + return "登录失败"; + } + // 抓取图片到指定文件夹 + + ThreadGetOnceTempData threadGetOnceTempData = new ThreadGetOnceTempData(); + String result = threadGetOnceTempData.threadGetOnceTempData(lUserID, 1); + CaptureJPEGPicture captureJPEGPicture = new CaptureJPEGPicture(); + captureJPEGPicture.captureJPEGPicture(lUserID, iChannelNum, (short) 9, imagefilename); + // 登出 + hCNetSDK.NET_DVR_Logout(lUserID); + return result; + } + + /********************************** + * 用途说明: 大立热成像温度获取 + * 参数说明 ip 摄像机ip + * 参数说明 port 摄像机端口 + * 参数说明 username 用户名 + * 参数说明 password 密码 + * 参数说明 region 测温区域 + * 返回值说明: java.lang.String + ***********************************/ + private static String getDaLiTemperature(String ip, short port, String username, String password, + String outsideAngle, String csvFilePath) { + + // 获取spring bean 中 HttpRESTfulUtils 类实例 + HttpRESTfulUtils httpUtil = SpringContextHolder.getBean(HttpRESTfulUtils.class); + cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(outsideAngle); + String type = jsonObject.getStr("type"); + JSONArray pos = jsonObject.getJSONArray("pos"); + cn.hutool.json.JSONObject posObject1 = pos.getJSONObject(0); + File srcImgFile = new File(csvFilePath); + //文件转化为图片 + Image srcImg = null; + try { + srcImg = ImageIO.read(srcImgFile); + } catch (IOException e) { + log.error(e.getMessage());; + } + if (srcImg == null) { + return ""; + } + //获取图片的宽 + int srcImgWidth = srcImg.getWidth(null); + //获取图片的高 + int srcImgHeight = srcImg.getHeight(null); + // 获取图片比例 + int width = srcImgWidth / 640; + int height = srcImgHeight / 480; + int x1 = Convert.convert(Integer.class, posObject1.getStr("x")) == 0 ? 0 : Convert.convert(Integer.class, + posObject1.getStr("x")) / width; + int y1 = Convert.convert(Integer.class, posObject1.getStr("y")) == 0 ? 0 : Convert.convert(Integer.class, + posObject1.getStr("y")) / height; + if ("point".equals(type)) { + // 点测温 + String api = "cgi-bin/system?Module=DMMeasure&Type=Point&Index=0&Enable=On&StartX=" + x1 + "&StartY=" + y1; + // 设置点测温区域 + JSONObject pointData = httpUtil.sendHttpPostStr(ip, port + "", api, null, null); + String code = pointData.getString("code"); + if (!SystemCode.SUCCESS_STATUS_CODE.getCode().equals(code)) { + return null; + } + String selectApi = "cgi-bin/system?Module=DMTtl"; + JSONObject tempData = httpUtil.sendHttpGetStr(ip, port + "", selectApi, null); + String data = tempData.getString("data"); + String[] split = data.split("&"); + for (String dataStr : split) { + if (!dataStr.startsWith("Point=")) { + continue; + } + String removePrefix = StrUtil.removePrefix(dataStr, "Point="); + // 找到第一个 "@" 的位置 + int endIndex = removePrefix.indexOf("@"); + if (endIndex != -1) { + // 截取 "Point=" 后面并且第一个 "@" 前面的部分 + String substring = removePrefix.substring(0, endIndex); + String[] split1 = substring.split("\\|"); + String result = ""; + if (split1.length == 3) { + result = split1[1] + "," + split1[1]; + } + return result; + } else { + return ""; + } + } + } else { + // 框测温 + // 去除左上角和右下角坐标 + cn.hutool.json.JSONObject posObject2 = pos.getJSONObject(2); + int x2 = Convert.convert(Integer.class, posObject2.getStr("x")) == 0 ? 0 : Convert.convert(Integer.class, + posObject2.getStr("x")) / width; + int y2 = Convert.convert(Integer.class, posObject2.getStr("y")) == 0 ? 0 : Convert.convert(Integer.class, + posObject2.getStr("y")) / height; + String api = + "cgi-bin/system?Module=DMMeasure&Type=Area&Index=0&Enable=On&StartX=" + x1 + "&StartY=" + y1 + + "&EndX=" + x2 + "&EndY=" + y2; + // 设置框测温区域 + JSONObject areaData = httpUtil.sendHttpPostStr(ip, port + "", api, null, null); + String code = areaData.getString("code"); + if (!SystemCode.SUCCESS_STATUS_CODE.getCode().equals(code)) { + return null; + } + String selectApi = "cgi-bin/system?Module=DMTtl"; + JSONObject tempData = httpUtil.sendHttpGetStr(ip, port + "", selectApi, null); + String data = tempData.getString("data"); + String[] split = data.split("&"); + for (String dataStr : split) { + if (!dataStr.startsWith("Area=")) { + continue; + } + String removePrefix = StrUtil.removePrefix(dataStr, "Area="); + // 找到第一个 "@" 的位置 + int endIndex = removePrefix.indexOf("@"); + if (endIndex != -1) { + // 截取 "Point=" 后面并且第一个 "@" 前面的部分 + String substring = removePrefix.substring(0, endIndex); + String[] split1 = substring.split("\\|"); + String result = ""; + if (split1.length == 4) { + String index0 = split1[1]; + String[] split2 = index0.split(","); + result = split2[0] + "," + split2[1]; + } + return result; + } else { + return ""; + } + } + } + return null; + } + + /********************************** + * 用途说明: 海康热成像温度获取 + * 参数说明 ip 摄像机ip + * 参数说明 port 摄像机端口 + * 参数说明 username 用户名 + * 参数说明 password 密码 + * 参数说明 region 测温区域 + * 返回值说明: java.lang.String + ***********************************/ + public static String getHikTemperature(String ip, short port, String username, String password, String region, + String csvFilePath, String filePath) { + /*初始SDK*/ + hCNetSDK.NET_DVR_Init(); + // hCNetSDK.NET_DVR_SetLogToFile(3, "D:/JavaDemoLog", true); + + //登陆 + Login login = new Login(); + lUserID = login.getlUserID(ip, port, username, password); + if (lUserID == -1) { + return ""; + } + // 获取spring bean 中 HttpServerConfig 类实例 + // HttpServerConfig config = + // SpringContextHolder.getBean(HttpServerConfig.class); + JpegWithAppendData jpegWithAppendData = new JpegWithAppendData(); + String[][] temp = jpegWithAppendData.jpegWithAppendData1(lUserID, 2, csvFilePath); + cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(region); + String type = jsonObject.getStr("type"); + String result; + DecimalFormat format = new DecimalFormat("0.0"); + JSONArray pos = jsonObject.getJSONArray("pos"); + Object o = pos.get(0); + cn.hutool.json.JSONObject jsonObject1 = JSONUtil.parseObj(o); + File srcImgFile = new File(csvFilePath); + //文件转化为图片 + Image srcImg = null; + try { + srcImg = ImageIO.read(srcImgFile); + } catch (IOException e) { + log.error(e.getMessage());; + } + if (srcImg == null) { + return ""; + } + //获取图片的宽 + int srcImgWidth = srcImg.getWidth(null); + //获取图片的高 + int srcImgHeight = srcImg.getHeight(null); + // 获取图片比例 + int width = srcImgWidth / temp.length; + int height = srcImgHeight / temp[0].length; + int x1 = Convert.convert(Integer.class, jsonObject1.getStr("x")) == 0 ? 0 : Convert.convert(Integer.class, + jsonObject1.getStr("x")) / width; + int y1 = Convert.convert(Integer.class, jsonObject1.getStr("y")) == 0 ? 0 : Convert.convert(Integer.class, + jsonObject1.getStr("y")) / height; + if ("point".equals(type)) { + // 点测温 + // 坐标 + String max = format.format(Double.valueOf(temp[x1][y1])); + result = max + "," + max; + } else { + // 框测温 + // 去除左上角和右下角坐标 + Object o1 = pos.get(2); + cn.hutool.json.JSONObject jsonObject2 = JSONUtil.parseObj(o1); + int x2 = Convert.convert(Integer.class, jsonObject2.getStr("x")) == 0 ? 0 : Convert.convert(Integer.class + , jsonObject2.getStr("x")) / width; + int y2 = Convert.convert(Integer.class, jsonObject2.getStr("y")) == 0 ? 0 : Convert.convert(Integer.class + , jsonObject2.getStr("y")) / height; + List tempList = new ArrayList<>(); + for (int i = x1; i <= x2; i++) { + tempList.addAll(Arrays.asList(temp[i]).subList(y1, y2 + 1)); + } + Collections.sort(tempList); + + String max = format.format(Double.valueOf(tempList.get(tempList.size() - 1))); + String min = format.format(Double.valueOf(tempList.get(0))); + result = max + "," + min; + } + // 登出 + hCNetSDK.NET_DVR_Logout(lUserID); + return result; + } + + public static String getStorageStatus(String manufacturer, String ip, short port, String username, + String password) { + + // 根据生产厂家判断对应方法 + if ("dahua".equals(manufacturer)) { + //throw new RuntimeException("暂时没有 “大华” SDK"); + return ""; + } else if ("yushi".equals(manufacturer)) { + //throw new RuntimeException("暂时没有 “宇视” SDK"); + return ""; + } else if ("haikang".equals(manufacturer) || "Hikvision".equals(manufacturer)) { + log.info("进入getStorageStatus"); + return getHikStorageStatus(ip, port, username, password); + } else { + //throw new RuntimeException("暂时没有该生产厂家"); + return ""; + } + } + + //String manufacturer, String ip, short port, String username, + // String password + public static String getHikStorageStatus(String ip, short port, String username, String password) { + /*初始SDK*/ + hCNetSDK.NET_DVR_Init(); + // hCNetSDK.NET_DVR_SetLogToFile(3, "D:/JavaDemoLog", true); + + //登陆 + Login login = new Login(); + lUserID = login.getlUserID(ip, port, username, password); + if (lUserID == -1) { + return null; + } + AddOSD addOSD = new AddOSD(); + cn.hutool.json.JSONObject result = addOSD.getHDCFG(lUserID, 1); + + // 登出 + hCNetSDK.NET_DVR_Logout(lUserID); + return result.toString(); + } + + public static void addWaterMark(String srcImgPath, String tarImgPath, String waterMarkContent, + Color markContentColor, + Font font) { + + try { + // 读取原图片信息 + File srcImgFile = new File(srcImgPath);//得到文件 + Image srcImg = ImageIO.read(srcImgFile);//文件转化为图片 + int srcImgWidth = srcImg.getWidth(null);//获取图片的宽 + int srcImgHeight = srcImg.getHeight(null);//获取图片的高 + // 加水印 + BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bufImg.createGraphics(); + g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null); + g.setColor(markContentColor); //根据图片的背景设置水印颜色 + g.setFont(font); //设置字体 + + String[] split = waterMarkContent.split(","); + String max = "max:" + split[0] + "°C"; + String min = "min:" + split[1] + "°C"; + int height = srcImgHeight / 5; + //设置水印的坐标 + int x1 = srcImgWidth - 2 * getWatermarkLength(max, g); + int x2 = srcImgWidth - 2 * getWatermarkLength(min, g); + int y1 = srcImgHeight - height * 4; + int y2 = srcImgHeight - height; + // int x = srcImgWidth - 2 * getWatermarkLength(content, g); + // int y = srcImgHeight - getWatermarkLength(content, g); + //画出水印 + // g.drawString(content, x, y); + g.drawString(max, x1, y1); + g.drawString(min, x2, y2); + g.dispose(); + // 输出图片 + FileOutputStream outImgStream = new FileOutputStream(tarImgPath); + ImageIO.write(bufImg, "jpg", outImgStream); + log.info("-------------------添加温度完成-------------------"); + outImgStream.flush(); + outImgStream.close(); + + } catch (Exception e) { + // TODO: handle exception + } + } + + public static int getWatermarkLength(String waterMarkContent, Graphics2D g) { + return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length()); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/AddOSD.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/AddOSD.java new file mode 100644 index 0000000..1856de6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/AddOSD.java @@ -0,0 +1,126 @@ +package com.yfd.platform.modules.deviceapi.GeneralFunction; + +import cn.hutool.json.JSONException; +import cn.hutool.json.JSONObject; +import com.sun.jna.ptr.IntByReference; +import com.yfd.platform.modules.deviceapi.HCNetSDK; +import lombok.extern.slf4j.Slf4j; + +import java.io.UnsupportedEncodingException; + +/** + * Created by panting6 on 2022/4/14. + */ +@Slf4j +public class AddOSD { + + static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; + + public void addOSD(int lUserID, int iChannelNum) { + + HCNetSDK.NET_DVR_PICCFG_V40 piccfc40 = new HCNetSDK.NET_DVR_PICCFG_V40(); + piccfc40.dwSize = piccfc40.size(); + IntByReference lpBytesReturned = new IntByReference(0); + if (!hCNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_PICCFG_V40, iChannelNum, + piccfc40.getPointer(), piccfc40.size(), lpBytesReturned)) { + log.info("获得图像参数失败,错误号:" + hCNetSDK.NET_DVR_GetLastError()); + } else { + piccfc40.read(); + + String name = new String(piccfc40.sChanName); + + log.info("获得图像参数成功,通道号名称" + name + ",OSD属性:" + piccfc40.byOSDAttrib + ",字体大小:" + piccfc40.byFontSize + ",预览的图象上是否显示OSD:" + piccfc40.dwShowOsd); + + } + + piccfc40.sChanName = "camera 01".getBytes(); + piccfc40.dwVideoFormat = 2; + piccfc40.dwShowChanName = 1; + piccfc40.wShowNameTopLeftX = 496; + piccfc40.wShowNameTopLeftY = 512; + piccfc40.dwEnableHide = 0; + piccfc40.dwShowOsd = 1; + piccfc40.wOSDTopLeftX = 0; + piccfc40.wOSDTopLeftY = 32; + piccfc40.byOSDType = 1; + piccfc40.byDispWeek = 1; + piccfc40.byOSDAttrib = 4; + piccfc40.byHourOSDType = 0; + piccfc40.byFontSize = (byte) 0xff; + piccfc40.byOSDColorType = 0; + piccfc40.byAlignment = 0; + + piccfc40.write(); + + if (!hCNetSDK.NET_DVR_SetDVRConfig(lUserID, HCNetSDK.NET_DVR_SET_PICCFG_V40, iChannelNum, + piccfc40.getPointer(), piccfc40.size())) { + log.info("设置图像参数失败,错误号:" + hCNetSDK.NET_DVR_GetLastError()); + } + log.info("设置图像参数成功"); + } + + public JSONObject getHDCFG(int lUserID, int iChannelNum) throws JSONException { + + HCNetSDK.NET_DVR_HDCFG piccfc40 = new HCNetSDK.NET_DVR_HDCFG(); + piccfc40.dwSize = piccfc40.size(); + IntByReference lpBytesReturned = new IntByReference(0); + if (!hCNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_HDCFG, iChannelNum, piccfc40.getPointer(), + piccfc40.size(), lpBytesReturned)) { + log.info("获得图像参数失败,错误号:" + hCNetSDK.NET_DVR_GetLastError()); + return null; + } else { + piccfc40.read(); + + HCNetSDK.NET_DVR_SINGLE_HD[] struHDInfo = piccfc40.struHDInfo; + HCNetSDK.NET_DVR_SINGLE_HD netDvrSingleHd = struHDInfo[0]; + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("dwCapacity", netDvrSingleHd.dwCapacity); + jsonObject.putOnce("dwFreeSpace", netDvrSingleHd.dwFreeSpace); + jsonObject.putOnce("dwHdStatus", netDvrSingleHd.dwHdStatus); + jsonObject.putOnce("byHDAttr", netDvrSingleHd.byHDAttr); + jsonObject.putOnce("dwHDNo", netDvrSingleHd.dwHDNo); + jsonObject.putOnce("dwHdGroup", netDvrSingleHd.dwHdGroup); + return jsonObject; + } + + } + + public void showString(int lUserID, int iChannelNum) throws UnsupportedEncodingException { + HCNetSDK.NET_DVR_SHOWSTRING_V30 showstring30 = new HCNetSDK.NET_DVR_SHOWSTRING_V30(); + showstring30.dwSize = showstring30.size(); + IntByReference lpBytesReturned = new IntByReference(0); + if (!hCNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_SHOWSTRING_V30, iChannelNum, + showstring30.getPointer(), showstring30.size(), lpBytesReturned)) { + log.info("获取字符叠加失败,错误号:" + hCNetSDK.NET_DVR_GetLastError()); + } else { + showstring30.read(); + + String name = new String(showstring30.struStringInfo[0].sString); + + log.info("获取字符叠加成功,预览的图象上是否显示字符" + showstring30.struStringInfo[0].wShowString + ",该字符长度:" + + showstring30.struStringInfo[0].wStringSize + ",字符显示位置的x坐标:" + showstring30.struStringInfo[0].wShowStringTopLeftX + + ",字符显示位置的Y坐标:" + showstring30.struStringInfo[0].wShowStringTopLeftY + ",要显示的字符内容:" + name); + } + + showstring30.struStringInfo[0].wShowString = 1; + showstring30.struStringInfo[0].wStringSize = 9; + showstring30.struStringInfo[0].wShowStringTopLeftX = 16; + showstring30.struStringInfo[0].wShowStringTopLeftY = 96; + showstring30.struStringInfo[0].sString = "hhhhhh".getBytes(); + + if (!hCNetSDK.NET_DVR_SetDVRConfig(lUserID, HCNetSDK.NET_DVR_SET_SHOWSTRING_V30, iChannelNum, + showstring30.getPointer(), showstring30.size())) { + log.info("设置字符叠加失败,错误号:" + hCNetSDK.NET_DVR_GetLastError()); + } else { + showstring30.read(); + + String rename = new String(showstring30.struStringInfo[0].sString); + + log.info("设置字符叠加成功,预览的图象上是否显示字符" + showstring30.struStringInfo[0].wShowString + ",该字符长度:" + + showstring30.struStringInfo[0].wStringSize + ",字符显示位置的x坐标:" + showstring30.struStringInfo[0].wShowStringTopLeftX + + ",字符显示位置的Y坐标:" + showstring30.struStringInfo[0].wShowStringTopLeftY + ",要显示的字符内容:" + rename); + } + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/CaptureJPEGPicture.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/CaptureJPEGPicture.java new file mode 100644 index 0000000..e52b073 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/CaptureJPEGPicture.java @@ -0,0 +1,46 @@ +package com.yfd.platform.modules.deviceapi.GeneralFunction; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.yfd.platform.modules.deviceapi.HCNetSDK; +import lombok.extern.slf4j.Slf4j; + +/** + * Created by panting6 on 2021/8/12. + */ +@Slf4j +public class CaptureJPEGPicture { + + static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; + public String captureJPEGPicture(int lUserID, int iChannelNum,short picsize,String imagefilename) + { + HCNetSDK.NET_DVR_JPEGPARA lpJpegPara = new HCNetSDK.NET_DVR_JPEGPARA(); + lpJpegPara.wPicQuality = 1; + lpJpegPara.wPicSize = picsize; + /* SimpleDateFormat sf = new SimpleDateFormat("yyyymmddHHmmss"); + String newName = sf.format(new Date());*/ + if(!hCNetSDK.NET_DVR_CaptureJPEGPicture(lUserID,iChannelNum,lpJpegPara,imagefilename)) + { + return "设备抓图失败"; + } + else{ + return "设备抓图成功"; + } + } + + public void captureJPEGPictureNew(int lUserID, int iChannelNum, short picsize) + { + HCNetSDK.NET_DVR_JPEGPARA lpJegPara = new HCNetSDK.NET_DVR_JPEGPARA(); + lpJegPara.wPicQuality = 1; + lpJegPara.wPicSize = picsize; + Pointer struJpegPicBuffer = null; + IntByReference lpSizeReturned = new IntByReference(0); + if(!hCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(lUserID,iChannelNum,lpJegPara,struJpegPicBuffer,picsize,lpSizeReturned)) + { + log.error("设备抓图到内存失败,错误码:" + hCNetSDK.NET_DVR_GetLastError()); + } + else{ + log.info("设备抓图到内存成功,返回图片数据长度:" + lpSizeReturned); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/Login.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/Login.java new file mode 100644 index 0000000..08c3c34 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/GeneralFunction/Login.java @@ -0,0 +1,42 @@ +package com.yfd.platform.modules.deviceapi.GeneralFunction; + +import com.yfd.platform.modules.deviceapi.HCNetSDK; +import lombok.extern.slf4j.Slf4j; + +/** + * 登录 + */ +@Slf4j +public class Login { + static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; + public static int lUserID;//用户句柄 + + public int getlUserID(String m_sDeviceIP,short wPort,String m_sUsername,String m_sPassword){ + //注册 + HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();//设备登录信息 + + m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN]; + System.arraycopy(m_sDeviceIP.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, m_sDeviceIP.length()); + + m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN]; + System.arraycopy(m_sUsername.getBytes(), 0, m_strLoginInfo.sUserName, 0, m_sUsername.length()); + + m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN]; + System.arraycopy(m_sPassword.getBytes(), 0, m_strLoginInfo.sPassword, 0, m_sPassword.length()); + + m_strLoginInfo.wPort = wPort; + + m_strLoginInfo.bUseAsynLogin = false; //是否异步登录:0- 否,1- 是 + m_strLoginInfo.write(); + + HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();//设备信息 + lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo); + if (lUserID == -1) { + log.error("登录失败,错误码为" + hCNetSDK.NET_DVR_GetLastError()); + return -1; + } else { + log.info("登录成功!"); + return lUserID; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/HCNetSDK.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/HCNetSDK.java new file mode 100644 index 0000000..4d72b50 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/HCNetSDK.java @@ -0,0 +1,8620 @@ +/* + * HCNetSDK.java + * + */ + +package com.yfd.platform.modules.deviceapi; + +//import Test1.Thermometry.P2PRealData; +import com.sun.jna.*; +import com.sun.jna.examples.win32.GDI32.RECT; +import com.sun.jna.examples.win32.W32API; +import com.sun.jna.examples.win32.W32API.HWND; +import com.sun.jna.ptr.ByteByReference; +import com.sun.jna.win32.StdCallLibrary; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.NativeLongByReference; +import com.sun.jna.ptr.ShortByReference; +import com.yfd.platform.modules.deviceapi.CameraUtil; + +import java.io.UnsupportedEncodingException; + +//SDK接口说明,HCNetSDK.dll +public interface HCNetSDK extends Library { + + HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary(CameraUtil.DLL_PATH, HCNetSDK.class); + /***宏定义***/ + //常量 + + public static final int MAX_NAMELEN = 16; //DVR本地登陆名 + public static final int MAX_RIGHT = 32; //设备支持的权限(1-12表示本地权限,13-32表示远程权限) + public static final int NAME_LEN = 32; //用户名长度 + public static final int PASSWD_LEN = 16; //密码长度 + public static final int SERIALNO_LEN = 48; //序列号长度 + public static final int MACADDR_LEN = 6; //mac地址长度 + public static final int MAX_ETHERNET = 2; //设备可配以太网络 + public static final int PATHNAME_LEN = 128; //路径长度 + public static final int MAX_TIMESEGMENT_V30 = 8; //9000设备最大时间段数 + public static final int MAX_TIMESEGMENT = 4; //8000设备最大时间段数 + public static final int MAX_SHELTERNUM = 4; //8000设备最大遮挡区域数 + public static final int MAX_DAYS = 7; //每周天数 + public static final int PHONENUMBER_LEN = 32; //pppoe拨号号码最大长度 + public static final int MAX_DISKNUM_V30 = 33; //9000设备最大硬盘数/* 最多33个硬盘(包括16个内置SATA硬盘、1个eSATA硬盘和16个NFS盘) */ + public static final int MAX_DISKNUM = 16; //8000设备最大硬盘数 + public static final int MAX_DISKNUM_V10 = 8; //1.2版本之前版本 + public static final int MAX_WINDOW_V30 = 32; //9000设备本地显示最大播放窗口数 + public static final int MAX_WINDOW = 16; //8000设备最大硬盘数 + public static final int MAX_VGA_V30 = 4; //9000设备最大可接VGA数 + public static final int MAX_VGA = 1; //8000设备最大可接VGA数 + public static final int MAX_USERNUM_V30 = 32; //9000设备最大用户数 + public static final int MAX_USERNUM = 16; //8000设备最大用户数 + public static final int MAX_EXCEPTIONNUM_V30 = 32; //9000设备最大异常处理数 + public static final int MAX_EXCEPTIONNUM = 16; //8000设备最大异常处理数 + public static final int MAX_LINK = 6; //8000设备单通道最大视频流连接数 + public static final int MAX_DECPOOLNUM = 4; //单路解码器每个解码通道最大可循环解码数 + public static final int MAX_DECNUM = 4; //单路解码器的最大解码通道数(实际只有一个,其他三个保留) + public static final int MAX_TRANSPARENTNUM = 2; //单路解码器可配置最大透明通道数 + public static final int MAX_CYCLE_CHAN = 16; //单路解码器最大轮循通道数 + public static final int MAX_DIRNAME_LENGTH = 80; //最大目录长度 + public static final int MAX_STRINGNUM_V30 = 8; //9000设备最大OSD字符行数数 + public static final int MAX_STRINGNUM = 4; //8000设备最大OSD字符行数数 + public static final int MAX_STRINGNUM_EX = 8; //8000定制扩展 + public static final int MAX_AUXOUT_V30 = 16; //9000设备最大辅助输出数 + public static final int MAX_AUXOUT = 4; //8000设备最大辅助输出数 + public static final int MAX_HD_GROUP = 16; //9000设备最大硬盘组数 + public static final int MAX_NFS_DISK = 8; //8000设备最大NFS硬盘数 + public static final int IW_ESSID_MAX_SIZE = 32; //WIFI的SSID号长度 + public static final int IW_ENCODING_TOKEN_MAX = 32; //WIFI密锁最大字节数 + public static final int MAX_SERIAL_NUM = 64; //最多支持的透明通道路数 + public static final int MAX_DDNS_NUMS = 10; //9000设备最大可配ddns数 + public static final int MAX_DOMAIN_NAME = 64; /* 最大域名长度 */ + + public static final int MAX_EMAIL_ADDR_LEN = 48; //最大email地址长度 + public static final int MAX_EMAIL_PWD_LEN = 32; //最大email密码长度 + public static final int MAXPROGRESS = 100; //回放时的最大百分率 + public static final int MAX_SERIALNUM = 2; //8000设备支持的串口数 1-232, 2-485 + public static final int CARDNUM_LEN = 20; //卡号长度 + public static final int MAX_VIDEOOUT_V30 = 4; //9000设备的视频输出数 + public static final int MAX_VIDEOOUT = 2; //8000设备的视频输出数 + public static final int MAX_PRESET_V30 = 256; /* 9000设备支持的云台预置点数 */ + public static final int MAX_TRACK_V30 = 256; /* 9000设备支持的云台轨迹数 */ + public static final int MAX_CRUISE_V30 = 256; /* 9000设备支持的云台巡航数 */ + public static final int MAX_PRESET = 128; /* 8000设备支持的云台预置点数 */ + public static final int MAX_TRACK = 128; /* 8000设备支持的云台轨迹数 */ + public static final int MAX_CRUISE = 128; /* 8000设备支持的云台巡航数 */ + public static final int CRUISE_MAX_PRESET_NUMS = 32; /* 一条巡航最多的巡航点 */ + public static final int MAX_SERIAL_PORT = 8; //9000设备支持232串口数 + public static final int MAX_PREVIEW_MODE = 8; /* 设备支持最大预览模式数目 1画面,4画面,9画面,16画面.... */ + public static final int MAX_MATRIXOUT = 16; /* 最大模拟矩阵输出个数 */ + public static final int LOG_INFO_LEN = 11840; /* 日志附加信息 */ + public static final int DESC_LEN = 16; /* 云台描述字符串长度 */ + public static final int PTZ_PROTOCOL_NUM = 200; /* 9000最大支持的云台协议数 */ + public static final int MAX_AUDIO = 1; //8000语音对讲通道数 + public static final int MAX_AUDIO_V30 = 2; //9000语音对讲通道数 + public static final int MAX_CHANNUM = 16; //8000设备最大通道数 + public static final int MAX_ALARMIN = 16; //8000设备最大报警输入数 + public static final int MAX_ALARMOUT = 4; //8000设备最大报警输出数 +//9000 IPC接入 + public static final int MAX_ANALOG_CHANNUM = 32; //最大32个模拟通道 + public static final int MAX_ANALOG_ALARMOUT = 32; //最大32路模拟报警输出 + public static final int MAX_ANALOG_ALARMIN = 32; //最大32路模拟报警输入 + public static final int MAX_IP_ALARMIN_V40 = 4096; //允许加入的最多报警输入数 + public static final int MAX_IP_ALARMOUT_V40 = 4096; //允许加入的最多报警输出数 + public static final int MAX_ALARMOUT_V40 = (MAX_IP_ALARMOUT_V40 + MAX_ANALOG_ALARMOUT); //4128 + public static final int MAX_ALARMIN_V40 = (MAX_IP_ALARMIN_V40 +MAX_ANALOG_ALARMOUT); //4128 + public static final int MAX_CHANNUM_V40 = 512; + public static final int MAX_IP_DEVICE = 32; //允许接入的最大IP设备数 + public static final int MAX_IP_CHANNEL = 32; //允许加入的最多IP通道数 + public static final int MAX_IP_ALARMIN = 128; //允许加入的最多报警输入数 + public static final int MAX_IP_ALARMOUT = 64; //允许加入的最多报警输出数 + + /* 最大支持的通道数 最大模拟加上最大IP支持 */ + public static final int MAX_CHANNUM_V30 = (MAX_ANALOG_CHANNUM + MAX_IP_CHANNEL);//64 + public static final int MAX_ALARMOUT_V30 = (MAX_ANALOG_ALARMOUT + MAX_IP_ALARMOUT);//96 + public static final int MAX_ALARMIN_V30 = (MAX_ANALOG_ALARMIN + MAX_IP_ALARMIN);//160 + public static final int MAX_IP_DEVICE_V40 = 64; + public static final int STREAM_ID_LEN = 32; + + public static final int MAX_LICENSE_LEN = 16; + public static final int MAX_LICENSE_LEN_EX = 32; //车牌号最大长度 + public static final int MAX_CARDNO_LEN = 48; //卡号最大长度 + public static final int VCA_MAX_POLYGON_POINT_NUM = 10; + + public static final int MAX_ID_NUM_LEN = 32; //最大身份证号长度 + public static final int MAX_ID_NAME_LEN = 128; //最大姓名长度 + public static final int MAX_ID_ADDR_LEN = 280; //最大住址长度 + public static final int MAX_ID_ISSUING_AUTHORITY_LEN = 128; //最大签发机关长度 + public static final int MAX_CARD_READER_NUM_512 = 512; //最大读卡器数 + public static final int ERROR_MSG_LEN = 32; //下发错误信息 + public static final int MAX_FACE_NUM = 2; //最大人脸数 + public static final int MAX_FINGER_PRINT_LEN = 768; //最大指纹长度 + + public static final int DEV_TYPE_NAME_LEN = 24; //设备类型名称长度 + public static final int MAX_FACE_PIC_NUM = 30; /*人脸子图个数*/ + public static final int CARDNUM_LEN_V30 = 40; + + public static final int MAX_NOTICE_NUMBER_LEN = 32; //公告编号最大长度 + public static final int MAX_NOTICE_THEME_LEN = 64; //公告主题最大长度 + public static final int MAX_NOTICE_DETAIL_LEN = 1024; //公告详情最大长度 + public static final int MAX_NOTICE_PIC_NUM = 6; //公告信息最大图片数量 + public static final int MAX_DEV_NUMBER_LEN = 32; //设备编号最大长度 + public static final int LOCK_NAME_LEN = 32; //锁名称 + + public static final int NET_SDK_EMPLOYEE_NO_LEN = 32; //工号长度 + public static final int NET_SDK_UUID_LEN = 36; //UUID长度 + + public static final int MAX_INQUEST_CDRW_NUM = 4; //最大刻录机数目 + public static final int SUPPORT_PD_NUM = 16; + public static final int SUPPORT_ARRAY_NUM = 8; + public static final int SUPPORT_VD_NUM = 128; + public static final int SUPPORT_PD_NUM_ = 16; + public static final int SUPPORT_PD_NUM_PARTTWO = 8; + + public static final int CARDNUM_LEN_OUT = 32; //外部结构体卡号长度 + public static final int GUID_LEN = 16; //GUID长度 + + public static final int MAX_ROIDETECT_NUM = 8; //支持的ROI区域数 + public static final int MAX_LANERECT_NUM = 5; //最大车牌识别区域数 + public static final int MAX_FORTIFY_NUM = 10; //最大布防个数 + public static final int MAX_INTERVAL_NUM = 4; //最大时间间隔个数 + public static final int MAX_CHJC_NUM = 3; //最大车辆省份简称字符个数 + public static final int MAX_VL_NUM = 5; //最大虚拟线圈个数 + public static final int MAX_DRIVECHAN_NUM = 16; //最大车道数 + public static final int MAX_COIL_NUM = 3; //最大线圈个数 + public static final int MAX_SIGNALLIGHT_NUM = 6; //最大信号灯个数 + public static final int MAX_IOSPEED_GROUP_NUM = 4; //IO测速组个数 + public static final int MAX_IOOUT_NUM = 4; //最大IO输出口个数 + public static final int MAX_IOIN_NUM = 8; //最大IO输入口个数 + public static final int MAX_RELAY_NUM = 12; //继电器控制设备最大数 2013-11-04 + public static final int MAX_VEHICLE_TYPE_NUM = 8; //车辆信息管控最大数2013-11-04 + public static final int MAX_IOIN_NUMEX = 10; //最大IO输入口个数(扩展) + public static final int MAX_ITC_LANE_NUM = 6; //最大车道个数 + public static final int MAX_LANEAREA_NUM = 2; //单车道最大区域个数 + public static final int ITC_MAX_POLYGON_POINT_NUM = 20; //检测区域最多支持20个点的多边形 + public static final int MAX_ITC_SERIALCHECK_NUM = 8; //串口校验类型个数 + public static final int MAX_LIGHT_NUM = 6; //最大交通灯数 + public static final int MAX_VIDEO_INTERVAL_NUM = 2; //最大抓拍间隔数 + public static final int MAX_VIDEO_DETECT_LIGHT_NUM = 12; //视频检测的最大检测区域 + public static final int MAX_CALIB_RECOG_NUM = 2; //标定区域个数 + public static final int MAX_RS485_NUM = 12; //485口最大支持数 + public static final int MAX_MOBILE_POLYGON_NUM = 3; //移动布控支持最大牌识区域个数 + public static final int MAX_MOBILE_DETECTLINE_NUM = 3; //移动布控支持最大违规检测线个数 + public static final int MAX_IOOUT_K_NUM = 8; //K系列最大IO输出口个数 + + public static final int NET_SDK_MAX_FDID_LEN = 256; //人脸库ID最大长度 + public static final int NET_SDK_MAX_PICID_LEN = 256; //人脸ID最大长度 + public static final int NET_SDK_MAX_INDENTITY_KEY_LEN = 64; //交互操作口令长度 + + + /*******************全局错误码 begin**********************/ + public static final int NET_DVR_NOERROR = 0; //没有错误 + public static final int NET_DVR_PASSWORD_ERROR = 1; //用户名密码错误 + public static final int NET_DVR_NOENOUGHPRI = 2;//权限不足 + public static final int NET_DVR_NOINIT = 3;//没有初始化 + public static final int NET_DVR_CHANNEL_ERROR = 4; //通道号错误 + public static final int NET_DVR_OVER_MAXLINK = 5; //连接到DVR的客户端个数超过最大 + public static final int NET_DVR_VERSIONNOMATCH = 6; //版本不匹配 + public static final int NET_DVR_NETWORK_FAIL_CONNECT = 7;//连接服务器失败 + public static final int NET_DVR_NETWORK_SEND_ERROR = 8; //向服务器发送失败 + public static final int NET_DVR_NETWORK_RECV_ERROR = 9; //从服务器接收数据失败 + public static final int NET_DVR_NETWORK_RECV_TIMEOUT = 10; //从服务器接收数据超时 + public static final int NET_DVR_NETWORK_ERRORDATA = 11; //传送的数据有误 + public static final int NET_DVR_ORDER_ERROR = 12; //调用次序错误 + public static final int NET_DVR_OPERNOPERMIT = 13; //无此权限 + public static final int NET_DVR_COMMANDTIMEOUT = 14; //DVR命令执行超时 + public static final int NET_DVR_ERRORSERIALPORT = 15; //串口号错误 + public static final int NET_DVR_ERRORALARMPORT = 16; //报警端口错误 + public static final int NET_DVR_PARAMETER_ERROR = 17;//参数错误 + public static final int NET_DVR_CHAN_EXCEPTION = 18; //服务器通道处于错误状态 + public static final int NET_DVR_NODISK = 19; //没有硬盘 + public static final int NET_DVR_ERRORDISKNUM = 20; //硬盘号错误 + public static final int NET_DVR_DISK_FULL = 21; //服务器硬盘满 + public static final int NET_DVR_DISK_ERROR = 22;//服务器硬盘出错 + public static final int NET_DVR_NOSUPPORT = 23;//服务器不支持 + public static final int NET_DVR_BUSY = 24;//服务器忙 + public static final int NET_DVR_MODIFY_FAIL = 25;//服务器修改不成功 + public static final int NET_DVR_PASSWORD_FORMAT_ERROR = 26;//密码输入格式不正确 + public static final int NET_DVR_DISK_FORMATING = 27; //硬盘正在格式化,不能启动操作 + public static final int NET_DVR_DVRNORESOURCE = 28; //DVR资源不足 + public static final int NET_DVR_DVROPRATEFAILED = 29; //DVR操作失败 + public static final int NET_DVR_OPENHOSTSOUND_FAIL = 30; //打开PC声音失败 + public static final int NET_DVR_DVRVOICEOPENED = 31; //服务器语音对讲被占用 + public static final int NET_DVR_TIMEINPUTERROR = 32; //时间输入不正确 + public static final int NET_DVR_NOSPECFILE = 33; //回放时服务器没有指定的文件 + public static final int NET_DVR_CREATEFILE_ERROR = 34; //创建文件出错 + public static final int NET_DVR_FILEOPENFAIL = 35; //打开文件出错 + public static final int NET_DVR_OPERNOTFINISH = 36; //上次的操作还没有完成 + public static final int NET_DVR_GETPLAYTIMEFAIL = 37; //获取当前播放的时间出错 + public static final int NET_DVR_PLAYFAIL = 38; //播放出错 + public static final int NET_DVR_FILEFORMAT_ERROR = 39;//文件格式不正确 + public static final int NET_DVR_DIR_ERROR = 40; //路径错误 + public static final int NET_DVR_ALLOC_RESOURCE_ERROR = 41;//资源分配错误 + public static final int NET_DVR_AUDIO_MODE_ERROR = 42; //声卡模式错误 + public static final int NET_DVR_NOENOUGH_BUF = 43; //缓冲区太小 + public static final int NET_DVR_CREATESOCKET_ERROR = 44; //创建SOCKET出错 + public static final int NET_DVR_SETSOCKET_ERROR = 45; //设置SOCKET出错 + public static final int NET_DVR_MAX_NUM = 46; //个数达到最大 + public static final int NET_DVR_USERNOTEXIST = 47; //用户不存在 + public static final int NET_DVR_WRITEFLASHERROR = 48;//写FLASH出错 + public static final int NET_DVR_UPGRADEFAIL = 49;//DVR升级失败 + public static final int NET_DVR_CARDHAVEINIT = 50; //解码卡已经初始化过 + public static final int NET_DVR_PLAYERFAILED = 51; //调用播放库中某个函数失败 + public static final int NET_DVR_MAX_USERNUM = 52; //设备端用户数达到最大 + public static final int NET_DVR_GETLOCALIPANDMACFAIL = 53;//获得客户端的IP地址或物理地址失败 + public static final int NET_DVR_NOENCODEING = 54; //该通道没有编码 + public static final int NET_DVR_IPMISMATCH = 55; //IP地址不匹配 + public static final int NET_DVR_MACMISMATCH = 56;//MAC地址不匹配 + public static final int NET_DVR_UPGRADELANGMISMATCH = 57;//升级文件语言不匹配 + public static final int NET_DVR_MAX_PLAYERPORT = 58;//播放器路数达到最大 + public static final int NET_DVR_NOSPACEBACKUP = 59;//备份设备中没有足够空间进行备份 + public static final int NET_DVR_NODEVICEBACKUP = 60; //没有找到指定的备份设备 + public static final int NET_DVR_PICTURE_BITS_ERROR = 61; //图像素位数不符,限24色 + public static final int NET_DVR_PICTURE_DIMENSION_ERROR = 62;//图片高*宽超限, 限128*256 + public static final int NET_DVR_PICTURE_SIZ_ERROR = 63; //图片大小超限,限100K + public static final int NET_DVR_LOADPLAYERSDKFAILED = 64; //载入当前目录下Player Sdk出错 + public static final int NET_DVR_LOADPLAYERSDKPROC_ERROR = 65; //找不到Player Sdk中某个函数入口 + public static final int NET_DVR_LOADDSSDKFAILED = 66; //载入当前目录下DSsdk出错 + public static final int NET_DVR_LOADDSSDKPROC_ERROR = 67; //找不到DsSdk中某个函数入口 + public static final int NET_DVR_DSSDK_ERROR = 68; //调用硬解码库DsSdk中某个函数失败 + public static final int NET_DVR_VOICEMONOPOLIZE = 69; //声卡被独占 + public static final int NET_DVR_JOINMULTICASTFAILED = 70; //加入多播组失败 + public static final int NET_DVR_CREATEDIR_ERROR = 71; //建立日志文件目录失败 + public static final int NET_DVR_BINDSOCKET_ERROR = 72; //绑定套接字失败 + public static final int NET_DVR_SOCKETCLOSE_ERROR = 73; //socket连接中断,此错误通常是由于连接中断或目的地不可达 + public static final int NET_DVR_USERID_ISUSING = 74; //注销时用户ID正在进行某操作 + public static final int NET_DVR_SOCKETLISTEN_ERROR = 75; //监听失败 + public static final int NET_DVR_PROGRAM_EXCEPTION = 76; //程序异常 + public static final int NET_DVR_WRITEFILE_FAILED = 77; //写文件失败 + public static final int NET_DVR_FORMAT_READONLY = 78;//禁止格式化只读硬盘 + public static final int NET_DVR_WITHSAMEUSERNAME = 79;//用户配置结构中存在相同的用户名 + public static final int NET_DVR_DEVICETYPE_ERROR = 80; /*导入参数时设备型号不匹配*/ + public static final int NET_DVR_LANGUAGE_ERROR = 81; /*导入参数时语言不匹配*/ + public static final int NET_DVR_PARAVERSION_ERROR = 82; /*导入参数时软件版本不匹配*/ + public static final int NET_DVR_IPCHAN_NOTALIVE = 83; /*预览时外接IP通道不在线*/ + public static final int NET_DVR_RTSP_SDK_ERROR = 84; /*加载高清IPC通讯库StreamTransClient.dll失败*/ + public static final int NET_DVR_CONVERT_SDK_ERROR = 85; /*加载转码库失败*/ + public static final int NET_DVR_IPC_COUNT_OVERFLOW = 86; /*超出最大的ip接入通道数*/ + public static final int NET_PLAYM4_NOERROR = 500; //no error + public static final int NET_PLAYM4_PARA_OVER = 501;//input parameter is invalid; + public static final int NET_PLAYM4_ORDER_ERROR = 502;//The order of the function to be called is error. + public static final int NET_PLAYM4_TIMER_ERROR = 503;//Create multimedia clock failed; + public static final int NET_PLAYM4_DEC_VIDEO_ERROR = 504;//Decode video data failed. + public static final int NET_PLAYM4_DEC_AUDIO_ERROR = 505;//Decode audio data failed. + public static final int NET_PLAYM4_ALLOC_MEMORY_ERROR = 506; //Allocate memory failed. + public static final int NET_PLAYM4_OPEN_FILE_ERROR = 507; //Open the file failed. + public static final int NET_PLAYM4_CREATE_OBJ_ERROR = 508;//Create thread or event failed + public static final int NET_PLAYM4_CREATE_DDRAW_ERROR = 509;//Create DirectDraw object failed. + public static final int NET_PLAYM4_CREATE_OFFSCREEN_ERROR = 510;//failed when creating off-screen surface. + public static final int NET_PLAYM4_BUF_OVER = 511; //buffer is overflow + public static final int NET_PLAYM4_CREATE_SOUND_ERROR = 512; //failed when creating audio device. + public static final int NET_PLAYM4_SET_VOLUME_ERROR = 513;//Set volume failed + public static final int NET_PLAYM4_SUPPORT_FILE_ONLY = 514;//The function only support play file. + public static final int NET_PLAYM4_SUPPORT_STREAM_ONLY = 515;//The function only support play stream. + public static final int NET_PLAYM4_SYS_NOT_SUPPORT = 516;//System not support. + public static final int NET_PLAYM4_FILEHEADER_UNKNOWN = 517; //No file header. + public static final int NET_PLAYM4_VERSION_INCORRECT = 518; //The version of decoder and encoder is not adapted. + public static final int NET_PALYM4_INIT_DECODER_ERROR = 519; //Initialize decoder failed. + public static final int NET_PLAYM4_CHECK_FILE_ERROR = 520; //The file data is unknown. + public static final int NET_PLAYM4_INIT_TIMER_ERROR = 521; //Initialize multimedia clock failed. + public static final int NET_PLAYM4_BLT_ERROR = 522;//Blt failed. + public static final int NET_PLAYM4_UPDATE_ERROR = 523;//Update failed. + public static final int NET_PLAYM4_OPEN_FILE_ERROR_MULTI = 524; //openfile error, streamtype is multi + public static final int NET_PLAYM4_OPEN_FILE_ERROR_VIDEO = 525; //openfile error, streamtype is video + public static final int NET_PLAYM4_JPEG_COMPRESS_ERROR = 526; //JPEG compress error + public static final int NET_PLAYM4_EXTRACT_NOT_SUPPORT = 527; //Don't support the version of this file. + public static final int NET_PLAYM4_EXTRACT_DATA_ERROR = 528; //extract video data failed. + /*******************全局错误码 end**********************/ + /************************************************* + NET_DVR_IsSupport()返回值 + 1-9位分别表示以下信息(位与是TRUE)表示支持; + **************************************************/ + public static final int NET_DVR_SUPPORT_DDRAW = 0x01;//支持DIRECTDRAW,如果不支持,则播放器不能工作; + public static final int NET_DVR_SUPPORT_BLT = 0x02;//显卡支持BLT操作,如果不支持,则播放器不能工作; + public static final int NET_DVR_SUPPORT_BLTFOURCC = 0x04;//显卡BLT支持颜色转换,如果不支持,播放器会用软件方法作RGB转换; + public static final int NET_DVR_SUPPORT_BLTSHRINKX = 0x08;//显卡BLT支持X轴缩小;如果不支持,系统会用软件方法转换; + public static final int NET_DVR_SUPPORT_BLTSHRINKY = 0x10;//显卡BLT支持Y轴缩小;如果不支持,系统会用软件方法转换; + public static final int NET_DVR_SUPPORT_BLTSTRETCHX = 0x20;//显卡BLT支持X轴放大;如果不支持,系统会用软件方法转换; + public static final int NET_DVR_SUPPORT_BLTSTRETCHY = 0x40;//显卡BLT支持Y轴放大;如果不支持,系统会用软件方法转换; + public static final int NET_DVR_SUPPORT_SSE = 0x80;//CPU支持SSE指令,Intel Pentium3以上支持SSE指令; + public static final int NET_DVR_SUPPORT_MMX = 0x100;//CPU支持MMX指令集,Intel Pentium3以上支持SSE指令; + /**********************云台控制命令 begin*************************/ + public static final int LIGHT_PWRON = 2; /* 接通灯光电源 */ + public static final int WIPER_PWRON = 3; /* 接通雨刷开关 */ + public static final int FAN_PWRON = 4; /* 接通风扇开关 */ + public static final int HEATER_PWRON = 5; /* 接通加热器开关 */ + public static final int AUX_PWRON1 = 6; /* 接通辅助设备开关 */ + public static final int AUX_PWRON2 = 7; /* 接通辅助设备开关 */ + public static final int SET_PRESET = 8; /* 设置预置点 */ + public static final int CLE_PRESET = 9; /* 清除预置点 */ + public static final int ZOOM_IN = 11; /* 焦距以速度SS变大(倍率变大) */ + public static final int ZOOM_OUT = 12; /* 焦距以速度SS变小(倍率变小) */ + public static final int FOCUS_NEAR = 13; /* 焦点以速度SS前调 */ + public static final int FOCUS_FAR = 14; /* 焦点以速度SS后调 */ + public static final int IRIS_OPEN = 15; /* 光圈以速度SS扩大 */ + public static final int IRIS_CLOSE = 16; /* 光圈以速度SS缩小 */ + public static final int TILT_UP = 21; /* 云台以SS的速度上仰 */ + public static final int TILT_DOWN = 22; /* 云台以SS的速度下俯 */ + public static final int PAN_LEFT = 23; /* 云台以SS的速度左转 */ + public static final int PAN_RIGHT = 24; /* 云台以SS的速度右转 */ + public static final int UP_LEFT = 25; /* 云台以SS的速度上仰和左转 */ + public static final int UP_RIGHT = 26; /* 云台以SS的速度上仰和右转 */ + public static final int DOWN_LEFT = 27; /* 云台以SS的速度下俯和左转 */ + public static final int DOWN_RIGHT = 28; /* 云台以SS的速度下俯和右转 */ + public static final int PAN_AUTO = 29; /* 云台以SS的速度左右自动扫描 */ + public static final int FILL_PRE_SEQ = 30; /* 将预置点加入巡航序列 */ + public static final int SET_SEQ_DWELL = 31; /* 设置巡航点停顿时间 */ + public static final int SET_SEQ_SPEED = 32; /* 设置巡航速度 */ + public static final int CLE_PRE_SEQ = 33;/* 将预置点从巡航序列中删除 */ + public static final int STA_MEM_CRUISE = 34;/* 开始记录轨迹 */ + public static final int STO_MEM_CRUISE = 35;/* 停止记录轨迹 */ + public static final int RUN_CRUISE = 36; /* 开始轨迹 */ + public static final int RUN_SEQ = 37; /* 开始巡航 */ + public static final int STOP_SEQ = 38; /* 停止巡航 */ + public static final int GOTO_PRESET = 39; /* 快球转到预置点 */ + + + /**********************云台控制命令 end*************************/ + /************************************************* + 回放时播放控制命令宏定义 + NET_DVR_PlayBackControl + NET_DVR_PlayControlLocDisplay + NET_DVR_DecPlayBackCtrl的宏定义 + 具体支持查看函数说明和代码 + **************************************************/ + public static final int NET_DVR_PLAYSTART = 1;//开始播放 + public static final int NET_DVR_PLAYSTOP = 2;//停止播放 + public static final int NET_DVR_PLAYPAUSE = 3;//暂停播放 + public static final int NET_DVR_PLAYRESTART = 4;//恢复播放 + public static final int NET_DVR_PLAYFAST = 5;//快放 + public static final int NET_DVR_PLAYSLOW = 6;//慢放 + public static final int NET_DVR_PLAYNORMAL = 7;//正常速度 + public static final int NET_DVR_PLAYFRAME = 8;//单帧放 + public static final int NET_DVR_PLAYSTARTAUDIO = 9;//打开声音 + public static final int NET_DVR_PLAYSTOPAUDIO = 10;//关闭声音 + public static final int NET_DVR_PLAYAUDIOVOLUME = 11;//调节音量 + public static final int NET_DVR_PLAYSETPOS = 12;//改变文件回放的进度 + public static final int NET_DVR_PLAYGETPOS = 13;//获取文件回放的进度 + public static final int NET_DVR_PLAYGETTIME = 14;//获取当前已经播放的时间(按文件回放的时候有效) + public static final int NET_DVR_PLAYGETFRAME = 15;//获取当前已经播放的帧数(按文件回放的时候有效) + public static final int NET_DVR_GETTOTALFRAMES = 16;//获取当前播放文件总的帧数(按文件回放的时候有效) + public static final int NET_DVR_GETTOTALTIME = 17;//获取当前播放文件总的时间(按文件回放的时候有效) + public static final int NET_DVR_THROWBFRAME = 20;//丢B帧 + public static final int NET_DVR_SETSPEED = 24;//设置码流速度 + public static final int NET_DVR_KEEPALIVE = 25;//保持与设备的心跳(如果回调阻塞,建议2秒发送一次) + public static final int NET_DVR_SET_TRANS_TYPE = 32; //设置转码格式 + +//远程按键定义如下: +/* key value send to CONFIG program */ + public static final int KEY_CODE_1 = 1; + public static final int KEY_CODE_2 = 2; + public static final int KEY_CODE_3 = 3; + public static final int KEY_CODE_4 = 4; + public static final int KEY_CODE_5 = 5; + public static final int KEY_CODE_6 = 6; + public static final int KEY_CODE_7 = 7; + public static final int KEY_CODE_8 = 8; + public static final int KEY_CODE_9 = 9; + public static final int KEY_CODE_0 = 10; + public static final int KEY_CODE_POWER = 11; + public static final int KEY_CODE_MENU = 12; + public static final int KEY_CODE_ENTER = 13; + public static final int KEY_CODE_CANCEL = 14; + public static final int KEY_CODE_UP = 15; + public static final int KEY_CODE_DOWN = 16; + public static final int KEY_CODE_LEFT = 17; + public static final int KEY_CODE_RIGHT = 18; + public static final int KEY_CODE_EDIT = 19; + public static final int KEY_CODE_ADD = 20; + public static final int KEY_CODE_MINUS = 21; + public static final int KEY_CODE_PLAY = 22; + public static final int KEY_CODE_REC = 23; + public static final int KEY_CODE_PAN = 24; + public static final int KEY_CODE_M = 25; + public static final int KEY_CODE_A = 26; + public static final int KEY_CODE_F1 = 27; + public static final int KEY_CODE_F2 = 28; + + /* for PTZ control */ + public static final int KEY_PTZ_UP_START = KEY_CODE_UP; + public static final int KEY_PTZ_UP_STO = 32; + public static final int KEY_PTZ_DOWN_START = KEY_CODE_DOWN; + public static final int KEY_PTZ_DOWN_STOP = 33; + public static final int KEY_PTZ_LEFT_START = KEY_CODE_LEFT; + public static final int KEY_PTZ_LEFT_STOP = 34; + public static final int KEY_PTZ_RIGHT_START = KEY_CODE_RIGHT; + public static final int KEY_PTZ_RIGHT_STOP = 35; + public static final int KEY_PTZ_AP1_START = KEY_CODE_EDIT;/* 光圈+ */ + public static final int KEY_PTZ_AP1_STOP = 36; + public static final int KEY_PTZ_AP2_START = KEY_CODE_PAN;/* 光圈- */ + public static final int KEY_PTZ_AP2_STOP = 37; + public static final int KEY_PTZ_FOCUS1_START = KEY_CODE_A;/* 聚焦+ */ + public static final int KEY_PTZ_FOCUS1_STOP = 38; + public static final int KEY_PTZ_FOCUS2_START = KEY_CODE_M ;/* 聚焦- */ + public static final int KEY_PTZ_FOCUS2_STOP = 39; + public static final int KEY_PTZ_B1_START = 40;/* 变倍+ */ + public static final int KEY_PTZ_B1_STOP = 41; + public static final int KEY_PTZ_B2_START = 42;/* 变倍- */ + public static final int KEY_PTZ_B2_STOP = 43; +//9000新增 + public static final int KEY_CODE_11 = 44; + public static final int KEY_CODE_12 = 45; + public static final int KEY_CODE_13 = 46; + public static final int KEY_CODE_14 = 47; + public static final int KEY_CODE_15 = 48; + public static final int KEY_CODE_16 = 49; + /*************************参数配置命令 begin*******************************/ +//用于NET_DVR_SetDVRConfig和NET_DVR_GetDVRConfig,注意其对应的配置结构 + public static final int NET_DVR_GET_DEVICECFG = 100; //获取设备参数 + public static final int NET_DVR_SET_DEVICECFG = 101; //设置设备参数 + public static final int NET_DVR_GET_DEVICECFG_V40 = 1100; //获取扩展设备参数 + public static final int NET_DVR_SET_DEVICECFG_V40 = 1101; //设置扩展设备参数 + public static final int NET_DVR_GET_NETCFG = 102; //获取网络参数 + public static final int NET_DVR_SET_NETCFG = 103; //设置网络参数 + public static final int NET_DVR_GET_PICCFG = 104; //获取图象参数 + public static final int NET_DVR_SET_PICCFG = 105; //设置图象参数 + public static final int NET_DVR_GET_COMPRESSCFG = 106; //获取压缩参数 + public static final int NET_DVR_SET_COMPRESSCFG = 107; //设置压缩参数 + public static final int NET_DVR_GET_RECORDCFG = 108; //获取录像时间参数 + public static final int NET_DVR_SET_RECORDCFG = 109; //设置录像时间参数 + public static final int NET_DVR_GET_DECODERCFG = 110; //获取解码器参数 + public static final int NET_DVR_SET_DECODERCFG = 111; //设置解码器参数 + public static final int NET_DVR_GET_RS232CFG = 112; //获取232串口参数 + public static final int NET_DVR_SET_RS232CFG = 113; //设置232串口参数 + public static final int NET_DVR_GET_ALARMINCFG = 114; //获取报警输入参数 + public static final int NET_DVR_SET_ALARMINCFG = 115; //设置报警输入参数 + public static final int NET_DVR_GET_ALARMOUTCFG = 116; //获取报警输出参数 + public static final int NET_DVR_SET_ALARMOUTCFG = 117; //设置报警输出参数 + public static final int NET_DVR_GET_TIMECFG = 118; //获取DVR时间 + public static final int NET_DVR_SET_TIMECFG = 119; //设置DVR时间 + public static final int NET_DVR_GET_PREVIEWCFG = 120; //获取预览参数 + public static final int NET_DVR_SET_PREVIEWCFG = 121; //设置预览参数 + public static final int NET_DVR_GET_VIDEOOUTCFG = 122; //获取视频输出参数 + public static final int NET_DVR_SET_VIDEOOUTCFG = 123; //设置视频输出参数 + public static final int NET_DVR_GET_USERCFG = 124; //获取用户参数 + public static final int NET_DVR_SET_USERCFG = 125; //设置用户参数 + public static final int NET_DVR_GET_EXCEPTIONCFG = 126; //获取异常参数 + public static final int NET_DVR_SET_EXCEPTIONCFG = 127; //设置异常参数 + public static final int NET_DVR_GET_ZONEANDDST = 128; //获取时区和夏时制参数 + public static final int NET_DVR_SET_ZONEANDDST = 129; //设置时区和夏时制参数 + public static final int NET_DVR_GET_SHOWSTRING = 130; //获取叠加字符参数 + public static final int NET_DVR_SET_SHOWSTRING = 131; //设置叠加字符参数 + public static final int NET_DVR_GET_EVENTCOMPCFG = 132; //获取事件触发录像参数 + public static final int NET_DVR_SET_EVENTCOMPCFG = 133; //设置事件触发录像参数 + public static final int NET_DVR_GET_AUXOUTCFG = 140; //获取报警触发辅助输出设置(HS设备辅助输出2006-02-28) + public static final int NET_DVR_SET_AUXOUTCFG = 141; //设置报警触发辅助输出设置(HS设备辅助输出2006-02-28) + public static final int NET_DVR_GET_PREVIEWCFG_AUX = 142; //获取-s系列双输出预览参数(-s系列双输出2006-04-13) + public static final int NET_DVR_SET_PREVIEWCFG_AUX = 143; //设置-s系列双输出预览参数(-s系列双输出2006-04-13) + public static final int NET_DVR_GET_PICCFG_EX = 200; //获取图象参数(SDK_V14扩展命令) + public static final int NET_DVR_SET_PICCFG_EX = 201; //设置图象参数(SDK_V14扩展命令) + public static final int NET_DVR_GET_USERCFG_EX = 202; //获取用户参数(SDK_V15扩展命令) + public static final int NET_DVR_SET_USERCFG_EX = 203; //设置用户参数(SDK_V15扩展命令) + public static final int NET_DVR_GET_COMPRESSCFG_EX = 204; //获取压缩参数(SDK_V15扩展命令2006-05-15) + public static final int NET_DVR_SET_COMPRESSCFG_EX = 205; //设置压缩参数(SDK_V15扩展命令2006-05-15) + public static final int NET_DVR_GET_NETAPPCFG = 222; //获取网络应用参数 NTP/DDNS/EMAIL + public static final int NET_DVR_SET_NETAPPCFG = 223; //设置网络应用参数 NTP/DDNS/EMAIL + public static final int NET_DVR_GET_NTPCFG = 224; //获取网络应用参数 NTP + public static final int NET_DVR_SET_NTPCFG = 225; //设置网络应用参数 NTP + public static final int NET_DVR_GET_DDNSCFG = 226; //获取网络应用参数 DDNS + public static final int NET_DVR_SET_DDNSCFG = 227; //设置网络应用参数 DDNS +//对应NET_DVR_EMAILPARA + public static final int NET_DVR_GET_EMAILCFG = 228; //获取网络应用参数 EMAIL + public static final int NET_DVR_SET_EMAILCFG = 229; //设置网络应用参数 EMAIL + public static final int NET_DVR_GET_NFSCFG = 230; /* NFS disk config */ + public static final int NET_DVR_SET_NFSCFG = 231; /* NFS disk config */ + public static final int NET_DVR_GET_SHOWSTRING_EX = 238; //获取叠加字符参数扩展(支持8条字符) + public static final int NET_DVR_SET_SHOWSTRING_EX = 239; //设置叠加字符参数扩展(支持8条字符) + public static final int NET_DVR_GET_NETCFG_OTHER = 244; //获取网络参数 + public static final int NET_DVR_SET_NETCFG_OTHER = 245; //设置网络参数 +//对应NET_DVR_EMAILCFG结构 + public static final int NET_DVR_GET_EMAILPARACFG = 250; //Get EMAIL parameters + public static final int NET_DVR_SET_EMAILPARACFG = 251; //Setup EMAIL parameters + public static final int NET_DVR_GET_DDNSCFG_EX = 274;//获取扩展DDNS参数 + public static final int NET_DVR_SET_DDNSCFG_EX = 275;//设置扩展DDNS参数 + public static final int NET_DVR_SET_PTZPOS = 292; //云台设置PTZ位置 + public static final int NET_DVR_GET_PTZPOS = 293; //云台获取PTZ位置 + public static final int NET_DVR_GET_PTZSCOPE = 294;//云台获取PTZ范围 + + public static final int NET_DVR_COMPLETE_RESTORE_CTRL = 3420; //设置完全恢复出厂值 + /***************************DS9000新增命令(_V30) begin *****************************/ +//网络(NET_DVR_NETCFG_V30结构) + public static final int NET_DVR_GET_NETCFG_V30 = 1000; //获取网络参数 + public static final int NET_DVR_SET_NETCFG_V30 = 1001; //设置网络参数 +//图象(NET_DVR_PICCFG_V30结构) + public static final int NET_DVR_GET_PICCFG_V30 = 1002; //获取图象参数 + public static final int NET_DVR_SET_PICCFG_V30 = 1003; //设置图象参数 + public static final int NET_DVR_GET_PICCFG_V40 = 6179; //获取图象参数 + public static final int NET_DVR_SET_PICCFG_V40 = 6180; //设置图象参数 +//录像时间(NET_DVR_RECORD_V30结构) + public static final int NET_DVR_GET_RECORDCFG_V30 = 1004; //获取录像参数 + public static final int NET_DVR_SET_RECORDCFG_V30 = 1005; //设置录像参数 +//用户(NET_DVR_USER_V30结构) + public static final int NET_DVR_GET_USERCFG_V30 = 1006; //获取用户参数 + public static final int NET_DVR_SET_USERCFG_V30 = 1007; //设置用户参数 +//9000DDNS参数配置(NET_DVR_DDNSPARA_V30结构) + public static final int NET_DVR_GET_DDNSCFG_V30 = 1010; //获取DDNS(9000扩展) + public static final int NET_DVR_SET_DDNSCFG_V30 = 1011; //设置DDNS(9000扩展) +//EMAIL功能(NET_DVR_EMAILCFG_V30结构) + public static final int NET_DVR_GET_EMAILCFG_V30 = 1012;//获取EMAIL参数 + public static final int NET_DVR_SET_EMAILCFG_V30 = 1013;//设置EMAIL参数 +//巡航参数 (NET_DVR_CRUISE_PARA结构) + public static final int NET_DVR_GET_CRUISE = 1020; + public static final int NET_DVR_SET_CRUISE = 1021; +//报警输入结构参数 (NET_DVR_ALARMINCFG_V30结构) + public static final int NET_DVR_GET_ALARMINCFG_V30 = 1024; + public static final int NET_DVR_SET_ALARMINCFG_V30 = 1025; +//报警输出结构参数 (NET_DVR_ALARMOUTCFG_V30结构) + public static final int NET_DVR_GET_ALARMOUTCFG_V30 = 1026; + public static final int NET_DVR_SET_ALARMOUTCFG_V30 = 1027; +//视频输出结构参数 (NET_DVR_VIDEOOUT_V30结构) + public static final int NET_DVR_GET_VIDEOOUTCFG_V30 = 1028; + public static final int NET_DVR_SET_VIDEOOUTCFG_V30 = 1029; +//叠加字符结构参数 (NET_DVR_SHOWSTRING_V30结构) + public static final int NET_DVR_GET_SHOWSTRING_V30 = 1030; + public static final int NET_DVR_SET_SHOWSTRING_V30 = 1031; +//异常结构参数 (NET_DVR_EXCEPTION_V30结构) + public static final int NET_DVR_GET_EXCEPTIONCFG_V30 = 1034; + public static final int NET_DVR_SET_EXCEPTIONCFG_V30 = 1035; +//串口232结构参数 (NET_DVR_RS232CFG_V30结构) + public static final int NET_DVR_GET_RS232CFG_V30 = 1036; + public static final int NET_DVR_SET_RS232CFG_V30 = 1037; +//压缩参数 (NET_DVR_COMPRESSIONCFG_V30结构) + public static final int NET_DVR_GET_COMPRESSCFG_V30 = 1040; + public static final int NET_DVR_SET_COMPRESSCFG_V30 = 1041; +//获取485解码器参数 (NET_DVR_DECODERCFG_V30结构) + public static final int NET_DVR_GET_DECODERCFG_V30 = 1042; //获取解码器参数 + public static final int NET_DVR_SET_DECODERCFG_V30 = 1043; //设置解码器参数 +//获取预览参数 (NET_DVR_PREVIEWCFG_V30结构) + public static final int NET_DVR_GET_PREVIEWCFG_V30 = 1044; //获取预览参数 + public static final int NET_DVR_SET_PREVIEWCFG_V30 = 1045; //设置预览参数 +//辅助预览参数 (NET_DVR_PREVIEWCFG_AUX_V30结构) + public static final int NET_DVR_GET_PREVIEWCFG_AUX_V30 = 1046; //获取辅助预览参数 + public static final int NET_DVR_SET_PREVIEWCFG_AUX_V30 = 1047; //设置辅助预览参数 +//IP接入配置参数 (NET_DVR_IPPARACFG结构) + public static final int NET_DVR_GET_IPPARACFG = 1048; //获取IP接入配置信息 + public static final int NET_DVR_SET_IPPARACFG = 1049; //设置IP接入配置信息 +//IP接入配置参数V40 (NET_DVR_IPPARACFG_V40结构) + public static final int NET_DVR_GET_IPPARACFG_V40 = 1062; //获取IP接入配置信息 + public static final int NET_DVR_SET_IPPARACFG_V40 = 1063; //设置IP接入配置信息 +//IP报警输入接入配置参数 (NET_DVR_IPALARMINCFG结构) + public static final int NET_DVR_GET_IPALARMINCFG = 1050; //获取IP报警输入接入配置信息 + public static final int NET_DVR_SET_IPALARMINCFG = 1051; //设置IP报警输入接入配置信息 +//IP报警输出接入配置参数 (NET_DVR_IPALARMOUTCFG结构) + public static final int NET_DVR_GET_IPALARMOUTCFG = 1052; //获取IP报警输出接入配置信息 + public static final int NET_DVR_SET_IPALARMOUTCFG = 1053; //设置IP报警输出接入配置信息 +//硬盘管理的参数获取 (NET_DVR_HDCFG结构) + public static final int NET_DVR_GET_HDCFG = 1054; //获取硬盘管理配置参数 + public static final int NET_DVR_SET_HDCFG = 1055; //设置硬盘管理配置参数 +//盘组管理的参数获取 (NET_DVR_HDGROUP_CFG结构) + public static final int NET_DVR_GET_HDGROUP_CFG = 1056; //获取盘组管理配置参数 + public static final int NET_DVR_SET_HDGROUP_CFG = 1057; //设置盘组管理配置参数 +//设备编码类型配置(NET_DVR_COMPRESSION_AUDIO结构) + public static final int NET_DVR_GET_COMPRESSCFG_AUD = 1058; //获取设备语音对讲编码参数 + public static final int NET_DVR_SET_COMPRESSCFG_AUD = 1059; //设置设备语音对讲编码参数 + + public static final int NET_DVR_GET_VW_SCENE_PARAM = 1746;//获取电视墙场景模式参数 + public static final int NET_DVR_SET_VW_SCENE_PARAM = 1747;//设置电视墙场景模式参数 + + public static final int NET_SDK_FINDMEDICALFILE = 3954; //慧影科技智慧医疗查找录像文件 + public static final int NET_SDK_FINDMEDICALPICTURE = 3955; //慧影科技智慧医疗查找图片文件 + + public static final int NET_DVR_GET_RAPIDMOVE_DETECTION = 3539; //获取快速运动侦测配置 + public static final int NET_DVR_SET_RAPIDMOVE_DETECTION = 3540; //设置快速运动侦测配置 + + public static final int NET_DVR_GET_RAPIDMOVE_TRIGGER = 3543; //获取快速运动联动配置 + public static final int NET_DVR_SET_RAPIDMOVE_TRIGGER = 3544; //设置快速运动联动配置 + public static final int NET_DVR_GET_RAPIDMOVE_SCHEDULE = 3545; //获取快速运动的布防时间配置 + public static final int NET_DVR_SET_RAPIDMOVE_SCHEDULE = 3546; //设置快速运动的布防时间配置 + + public static final int NET_DVR_GET_PRESET_NAME = 3383; //获取预置点名称 + public static final int NET_DVR_SET_PRESET_NAME = 3382; //设置预置点名称 + public static final int NET_DVR_GET_RULECFG_V42 = 5049; //获取行为分析参数(支持16条规则扩展) + public static final int NET_DVR_SET_RULECFG_V42 = 5050; //设置行为分析参数(支持16条规则扩展) + + public static final int NET_DVR_GET_TRAVERSE_PLANE_DETECTION = 3360; //获取越界侦测配置 + public static final int NET_DVR_SET_TRAVERSE_PLANE_DETECTION = 3361; + public static final int NET_DVR_GET_FIELD_DETECTION = 3362; //获取区域侦测配置 + public static final int NET_DVR_SET_FIELD_DETECTION = 3363; //设置区域侦测配置 + + public static final int NET_DVR_GET_CCDPARAMCFG_EX = 3368;//获取前端参数(扩展) + public static final int NET_DVR_SET_CCDPARAMCFG_EX = 3369;//设置前端参数(扩展) + + public static final int NET_DVR_GET_STREAM_INFO = 6023; //获取已添加流ID信息 + public static final int NET_DVR_GET_STREAM_RECORD_STATUS = 6021; //获取流状态信息 + + public static final int NET_DVR_GET_ALL_VEHICLE_CONTROL_LIST = 3124; //获取所有车辆黑白名单信息 + public static final int NET_DVR_VEHICLELIST_CTRL_START = 3133; //设置车辆黑白名单信息(批量) + public static final int ENUM_SENDDATA = 0x0; //发送车辆黑白名单数据 + + public static final int NET_DVR_GET_LEDDISPLAY_CFG = 3673; + public static final int NET_DVR_SET_LEDDISPLAY_CFG = 3672; + public static final int NET_DVR_SET_VOICEBROADCAST_CFG = 3675; + public static final int NET_DVR_SET_CHARGE_ACCOUNTINFO = 3662; + + public static final int NET_DVR_GET_TRAFFIC_DATA = 3141; //长连接获取交通数据 + public static final int NET_DVR_GET_TRAFFIC_FLOW = 3142; //长连接获取交通流量 + + public static final int NET_DVR_GET_CARD_CFG = 2116; //获取卡参数 + public static final int NET_DVR_SET_CARD_CFG = 2117; //设置卡参数 + public static final int NET_DVR_GET_CARD_CFG_V50 = 2178; //获取新卡参数(V50) + public static final int NET_DVR_SET_CARD_CFG_V50 = 2179; //设置新卡参数(V50) + public static final int NET_DVR_GET_FACE_PARAM_CFG = 2507; //获取人脸参数 + public static final int NET_DVR_SET_FACE_PARAM_CFG = 2508; //设置人脸参数 + public static final int NET_DVR_DEL_FACE_PARAM_CFG = 2509; //删除人脸参数 + public static final int NET_DVR_GET_FINGERPRINT_CFG_V50 = 2183; //获取指纹参数V50 + public static final int NET_DVR_SET_FINGERPRINT_CFG_V50 = 2184; //设置指纹参数V50 + public static final int NET_DVR_DEL_FINGERPRINT_CFG_V50 = 2517; //删除指纹参数V50 + public static final int NET_DVR_GET_CARD_RIGHT_WEEK_PLAN_V50 = 2304; //获取卡权限周计划参数V50 + public static final int NET_DVR_SET_CARD_RIGHT_WEEK_PLAN_V50 = 2305; //设置卡权限周计划参数V50 + public static final int NET_DVR_GET_CARD_RIGHT_PLAN_TEMPLATE_V50 = 2322; //获取卡权限计划模板参数V50 + public static final int NET_DVR_SET_CARD_RIGHT_PLAN_TEMPLATE_V50 = 2323;//设置卡权限计划模板参数V50 + public static final int NET_DVR_GET_DOOR_CFG = 2108; //获取门参数 + public static final int NET_DVR_SET_DOOR_CFG = 2109; //设置门参数 + public static final int NET_DVR_GET_DOOR_STATUS_PLAN = 2110; //获取门状态计划参数 + public static final int NET_DVR_SET_DOOR_STATUS_PLAN = 2111; //设置门状态计划参数 + public static final int NET_DVR_GET_EVENT_CARD_LINKAGE_CFG_V50 = 2181; //获取事件卡号联动配置参数(V50) + public static final int NET_DVR_SET_EVENT_CARD_LINKAGE_CFG_V50 = 2182; //设置事件卡号联动配置参数(V50) + public static final int NET_DVR_CAPTURE_FACE_INFO = 2510; //采集人脸信息 + public static final int NET_DVR_GET_ACS_EVENT = 2514 ;//设备事件获取 + + public static final int NET_DVR_GET_SUPPLEMENTLIGHT = 3728; //获取内置补光灯配置协议 + public static final int NET_DVR_SET_SUPPLEMENTLIGHT = 3729; //设置内置补光灯配置协议 + + public static final int NET_DVR_GET_FACECONTRAST_TRIGGER = 3965;//获取人脸比对联动配置 + public static final int NET_DVR_SET_FACECONTRAST_TRIGGER = 3966;//设置人脸比对联动配置 + + public static final int NET_DVR_GET_FACECONTRAST_SCHEDULE = 3968;//获取人脸比对布防时间配置 + public static final int NET_DVR_SET_FACECONTRAST_SCHEDULE = 3969;//设置人脸比对布防时间配置 + + public static final int NET_DVR_INQUEST_GET_CDW_STATUS = 6350; //获取审讯机刻录状态-长连接 + //PTZ + public static final int NET_DVR_GET_PTZABSOLUTEEX = 6696; //获取高精度PTZ绝对位置配置 + public static final int NET_DVR_SET_PTZABSOLUTEEX = 6697; //设置高精度PTZ绝对位置配置 + + //测温 + public static final int NET_DVR_GET_THERMOMETRYRULE_TEMPERATURE_INFO = 23001;//手动获取测温规则温度信息 + public static final int NET_DVR_GET_REALTIME_THERMOMETRY = 3629; //实时温度检测 + public static final int NET_DVR_GET_MANUALTHERM_INFO = 6706; //手动测温实时获取 + public static final int NET_DVR_GET_THERMOMETRY_MODE = 6765 ;//获取测温模式参数 + public static final int NET_DVR_SET_THERMOMETRY_MODE = 6766 ;//设置测温模式参数 + public static final int NET_DVR_GET_THERMOMETRY_PRESETINFO = 3624; //获取测温预置点关联配置参数 + public static final int NET_DVR_SET_THERMOMETRY_PRESETINFO = 3625; //设置测温预置点关联配置参数 + + public static final int NET_DVR_GET_PHY_DISK_INFO = 6306; //获取物理磁盘信息 + public static final int NET_DVR_GET_WORK_STATUS = 6189; //获取设备工作状态 + + public static final int NET_DVR_GET_CURTRIGGERMODE = 3130; //获取设备当前触发模式 + public static final int NET_ITC_GET_TRIGGERCFG = 3003; //获取触发参数 + public static final int NET_ITC_SET_TRIGGERCFG = 3004; //设置触发参数 + public static final int NET_ITC_GET_VIDEO_TRIGGERCFG = 3017; //获取视频电警触发参数 + public static final int NET_ITC_SET_VIDEO_TRIGGERCFG = 3018; //设置视频电警触发参数 + + public static final int NET_DVR_GET_MULTI_STREAM_COMPRESSIONCFG = 3216;//远程获取多码流压缩参数 + public static final int NET_DVR_SET_MULTI_STREAM_COMPRESSIONCFG = 3217;//远程设置多码流压缩参数 + + public static final int NET_DVR_GET_CMS_CFG = 2070; + public static final int NET_DVR_SET_CMS_CFG = 2071; + + public static final int NET_DVR_GET_ALARM_INFO = 4193; //获取报警事件数据 + /***************************DS9000新增命令(_V30) end *****************************/ + + /*************************参数配置命令 end*******************************/ + /*******************查找文件和日志函数返回值*************************/ + public static final int NET_DVR_FILE_SUCCESS = 1000; //获得文件信息 + public static final int NET_DVR_FILE_NOFIND = 1001; //没有文件 + public static final int NET_DVR_ISFINDING = 1002;//正在查找文件 + public static final int NET_DVR_NOMOREFILE = 1003;//查找文件时没有更多的文件 + public static final int NET_DVR_FILE_EXCEPTION = 1004;//查找文件时异常 + /*********************回调函数类型 begin************************/ + public static final int COMM_ALARM = 0x1100; //8000报警信息主动上传 + public static final int COMM_TRADEINFO = 0x1500; //ATMDVR主动上传交易信息 + public static final int COMM_ALARM_V30 = 0x4000;//9000报警信息主动上传 + public static final int COMM_ALARM_V40 = 0x4007; + public static final int COMM_ALARM_RULE = 0x1102;//行为分析信息上传 + public static final int COMM_ALARM_PDC = 0x1103;//客流量统计报警上传 + public static final int COMM_UPLOAD_PLATE_RESULT = 0x2800;//交通抓拍结果上传 + public static final int COMM_ITS_PLATE_RESULT = 0x3050;//交通抓拍的终端图片上传 + public static final int COMM_IPCCFG = 0x4001;//9000设备IPC接入配置改变报警信息主动上传 + public static final int COMM_ITS_PARK_VEHICLE = 0x3056;//停车场数据上传 + public static final int COMM_VEHICLE_CONTROL_ALARM = 0x3059;//黑白名单车辆报警上传 + public static final int COMM_ALARM_TFS = 0x1113; //交通取证报警信息 + public static final int COMM_ALARM_TPS_V41 = 0x1114; //交通事件报警信息扩展 + public static final int COMM_ALARM_AID_V41 = 0x1115; //交通事件报警信息扩展 + public static final int COMM_UPLOAD_FACESNAP_RESULT = 0x1112; //人脸识别结果上传 + public static final int COMM_SNAP_MATCH_ALARM = 0x2902; //黑名单比对结果上传 + public static final int COMM_ALARM_ACS = 0x5002; //门禁主机报警信息 + public static final int COMM_ID_INFO_ALARM = 0x5200; //门禁身份证刷卡信息 + public static final int COMM_VCA_ALARM = 0x4993; //智能检测通用报警 + public static final int COMM_ISAPI_ALARM = 0x6009;//ISAPI协议报警信息 + public static final int COMM_ALARM_TPS_STATISTICS = 0x3082; //TPS统计过车数据上传 + public static final int COMM_ALARMHOST_CID_ALARM = 0x1127; //报告报警上传 + public static final int COMM_UPLOAD_VIDEO_INTERCOM_EVENT = 0x1132; //可视对讲事件记录上传 + public static final int COMM_ALARM_VIDEO_INTERCOM = 0x1133; //可视对讲报警上传 + public static final int COMM_THERMOMETRY_ALARM = 0x5212; //温度报警上传 + public static final int COMM_FIREDETECTION_ALARM = 0x4991; //火点报警上传 + + public static final int COMM_UPLOAD_AIOP_VIDEO = 0x4021; //设备支持AI开放平台接入,上传视频检测数据 + public static final int COMM_UPLOAD_AIOP_PICTURE = 0x4022; //设备支持AI开放平台接入,上传图片检测数据 + public static final int COMM_UPLOAD_AIOP_POLLING_SNAP = 0x4023; //设备支持AI开放平台接入,上传轮巡抓图图片检测数据 对应的结构体(NET_AIOP_POLLING_PICTURE_HEAD) + public static final int COMM_UPLOAD_AIOP_POLLING_VIDEO = 0x4024; //设备支持AI开放平台接入,上传轮巡视频检测数据 对应的结构体(NET_AIOP_POLLING_VIDEO_HEAD) + + /*************操作异常类型(消息方式, 回调方式(保留))****************/ + public static final int EXCEPTION_EXCHANGE = 0x8000;//用户交互时异常 + public static final int EXCEPTION_AUDIOEXCHANGE = 0x8001;//语音对讲异常 + public static final int EXCEPTION_ALARM = 0x8002;//报警异常 + public static final int EXCEPTION_PREVIEW = 0x8003;//网络预览异常 + public static final int EXCEPTION_SERIAL = 0x8004;//透明通道异常 + public static final int EXCEPTION_RECONNECT = 0x8005; //预览时重连 + public static final int EXCEPTION_ALARMRECONNECT = 0x8006;//报警时重连 + public static final int EXCEPTION_SERIALRECONNECT = 0x8007;//透明通道重连 + public static final int EXCEPTION_PLAYBACK = 0x8010;//回放异常 + public static final int EXCEPTION_DISKFMT = 0x8011;//硬盘格式化 + /********************预览回调函数*********************/ + public static final int NET_DVR_SYSHEAD = 1;//系统头数据 + public static final int NET_DVR_STREAMDATA = 2;//视频流数据(包括复合流和音视频分开的视频流数据) + public static final int NET_DVR_AUDIOSTREAMDATA = 3;//音频流数据 + public static final int NET_DVR_STD_VIDEODATA = 4;//标准视频流数据 + public static final int NET_DVR_STD_AUDIODATA = 5;//标准音频流数据 +//回调预览中的状态和消息 + public static final int NET_DVR_REALPLAYEXCEPTION = 111;//预览异常 + public static final int NET_DVR_REALPLAYNETCLOSE = 112;//预览时连接断开 + public static final int NET_DVR_REALPLAY5SNODATA = 113;//预览5s没有收到数据 + public static final int NET_DVR_REALPLAYRECONNECT = 114;//预览重连 + /********************回放回调函数*********************/ + public static final int NET_DVR_PLAYBACKOVER = 101;//回放数据播放完毕 + public static final int NET_DVR_PLAYBACKEXCEPTION = 102;//回放异常 + public static final int NET_DVR_PLAYBACKNETCLOSE = 103;//回放时候连接断开 + public static final int NET_DVR_PLAYBACK5SNODATA = 104; //回放5s没有收到数据 + /*********************回调函数类型 end************************/ +//设备型号(DVR类型) +/* 设备类型 */ + public static final int DVR = 1; /*对尚未定义的dvr类型返回NETRET_DVR*/ + public static final int ATMDVR = 2; /*atm dvr*/ + public static final int DVS = 3; /*DVS*/ + public static final int DEC = 4; /* 6001D */ + public static final int ENC_DEC = 5; /* 6001F */ + public static final int DVR_HC = 6; /*8000HC*/ + public static final int DVR_HT = 7; /*8000HT*/ + public static final int DVR_HF = 8; /*8000HF*/ + public static final int DVR_HS = 9; /* 8000HS DVR(no audio) */ + public static final int DVR_HTS = 10; /* 8016HTS DVR(no audio) */ + public static final int DVR_HB = 11; /* HB DVR(SATA HD) */ + public static final int DVR_HCS = 12; /* 8000HCS DVR */ + public static final int DVS_A = 13; /* 带ATA硬盘的DVS */ + public static final int DVR_HC_S = 14; /* 8000HC-S */ + public static final int DVR_HT_S = 15; /* 8000HT-S */ + public static final int DVR_HF_S = 16; /* 8000HF-S */ + public static final int DVR_HS_S = 17; /* 8000HS-S */ + public static final int ATMDVR_S = 18; /* ATM-S */ + public static final int LOWCOST_DVR = 19; /*7000H系列*/ + public static final int DEC_MAT = 20; /*多路解码器*/ + public static final int DVR_MOBILE = 21; /* mobile DVR */ + public static final int DVR_HD_S = 22; /* 8000HD-S */ + public static final int DVR_HD_SL = 23; /* 8000HD-SL */ + public static final int DVR_HC_SL = 24; /* 8000HC-SL */ + public static final int DVR_HS_ST = 25; /* 8000HS_ST */ + public static final int DVS_HW = 26; /* 6000HW */ + public static final int IPCAM = 30; /*IP 摄像机*/ + public static final int MEGA_IPCAM = 31; /*X52MF系列,752MF,852MF*/ + public static final int IPCAM_X62MF = 32; /*X62MF系列可接入9000设备,762MF,862MF*/ + public static final int IPDOME = 40; /*IP标清快球*/ + public static final int MEGA_IPDOME = 41; /*IP高清快球*/ + public static final int IPMOD = 50; /*IP 模块*/ + public static final int DS71XX_H = 71; /* DS71XXH_S */ + public static final int DS72XX_H_S = 72; /* DS72XXH_S */ + public static final int DS73XX_H_S = 73; /* DS73XXH_S */ + public static final int DS81XX_HS_S = 81; /* DS81XX_HS_S */ + public static final int DS81XX_HL_S = 82; /* DS81XX_HL_S */ + public static final int DS81XX_HC_S = 83; /* DS81XX_HC_S */ + public static final int DS81XX_HD_S = 84; /* DS81XX_HD_S */ + public static final int DS81XX_HE_S = 85; /* DS81XX_HE_S */ + public static final int DS81XX_HF_S = 86; /* DS81XX_HF_S */ + public static final int DS81XX_AH_S = 87; /* DS81XX_AH_S */ + public static final int DS81XX_AHF_S = 88; /* DS81XX_AHF_S */ + public static final int DS90XX_HF_S = 90; /*DS90XX_HF_S*/ + public static final int DS91XX_HF_S = 91; /*DS91XX_HF_S*/ + public static final int DS91XX_HD_S = 92; /*91XXHD-S(MD)*/ + + /* 操作 */ +//主类型 + public static final int MAJOR_OPERATION = 0x3; +//次类型 + public static final int MINOR_START_DVR = 0x41; /* 开机 */ + public static final int MINOR_STOP_DVR = 0x42;/* 关机 */ + public static final int MINOR_STOP_ABNORMAL = 0x43;/* 异常关机 */ + public static final int MINOR_REBOOT_DVR = 0x44; /*本地重启设备*/ + public static final int MINOR_LOCAL_LOGIN = 0x50; /* 本地登陆 */ + public static final int MINOR_LOCAL_LOGOUT = 0x51; /* 本地注销登陆 */ + public static final int MINOR_LOCAL_CFG_PARM = 0x52; /* 本地配置参数 */ + public static final int MINOR_LOCAL_PLAYBYFILE = 0x53; /* 本地按文件回放或下载 */ + public static final int MINOR_LOCAL_PLAYBYTIME = 0x54; /* 本地按时间回放或下载*/ + public static final int MINOR_LOCAL_START_REC = 0x55; /* 本地开始录像 */ + public static final int MINOR_LOCAL_STOP_REC = 0x56; /* 本地停止录像 */ + public static final int MINOR_LOCAL_PTZCTRL = 0x57; /* 本地云台控制 */ + public static final int MINOR_LOCAL_PREVIEW = 0x58;/* 本地预览 (保留不使用)*/ + public static final int MINOR_LOCAL_MODIFY_TIME = 0x59;/* 本地修改时间(保留不使用) */ + public static final int MINOR_LOCAL_UPGRADE = 0x5a;/* 本地升级 */ + public static final int MINOR_LOCAL_RECFILE_OUTPUT = 0x5b; /* 本地备份录象文件 */ + public static final int MINOR_LOCAL_FORMAT_HDD = 0x5c; /* 本地初始化硬盘 */ + public static final int MINOR_LOCAL_CFGFILE_OUTPUT = 0x5d; /* 导出本地配置文件 */ + public static final int MINOR_LOCAL_CFGFILE_INPUT = 0x5e; /* 导入本地配置文件 */ + public static final int MINOR_LOCAL_COPYFILE = 0x5f; /* 本地备份文件 */ + public static final int MINOR_LOCAL_LOCKFILE = 0x60; /* 本地锁定录像文件 */ + public static final int MINOR_LOCAL_UNLOCKFILE = 0x61; /* 本地解锁录像文件 */ + public static final int MINOR_LOCAL_DVR_ALARM = 0x62; /* 本地手动清除和触发报警*/ + public static final int MINOR_IPC_ADD = 0x63; /* 本地添加IPC */ + public static final int MINOR_IPC_DEL = 0x64; /* 本地删除IPC */ + public static final int MINOR_IPC_SET = 0x65; /* 本地设置IPC */ + public static final int MINOR_LOCAL_START_BACKUP = 0x66; /* 本地开始备份 */ + public static final int MINOR_LOCAL_STOP_BACKUP = 0x67;/* 本地停止备份*/ + public static final int MINOR_LOCAL_COPYFILE_START_TIME = 0x68;/* 本地备份开始时间*/ + public static final int MINOR_LOCAL_COPYFILE_END_TIME = 0x69; /* 本地备份结束时间*/ + public static final int MINOR_REMOTE_LOGIN = 0x70;/* 远程登录 */ + public static final int MINOR_REMOTE_LOGOUT = 0x71;/* 远程注销登陆 */ + public static final int MINOR_REMOTE_START_REC = 0x72;/* 远程开始录像 */ + public static final int MINOR_REMOTE_STOP_REC = 0x73;/* 远程停止录像 */ + public static final int MINOR_START_TRANS_CHAN = 0x74;/* 开始透明传输 */ + public static final int MINOR_STOP_TRANS_CHAN = 0x75; /* 停止透明传输 */ + public static final int MINOR_REMOTE_GET_PARM = 0x76;/* 远程获取参数 */ + public static final int MINOR_REMOTE_CFG_PARM = 0x77;/* 远程配置参数 */ + public static final int MINOR_REMOTE_GET_STATUS = 0x78;/* 远程获取状态 */ + public static final int MINOR_REMOTE_ARM = 0x79; /* 远程布防 */ + public static final int MINOR_REMOTE_DISARM = 0x7a;/* 远程撤防 */ + public static final int MINOR_REMOTE_REBOOT = 0x7b; /* 远程重启 */ + public static final int MINOR_START_VT = 0x7c;/* 开始语音对讲 */ + public static final int MINOR_STOP_VT = 0x7d;/* 停止语音对讲 */ + public static final int MINOR_REMOTE_UPGRADE = 0x7e; /* 远程升级 */ + public static final int MINOR_REMOTE_PLAYBYFILE = 0x7f; /* 远程按文件回放 */ + public static final int MINOR_REMOTE_PLAYBYTIME = 0x80; /* 远程按时间回放 */ + public static final int MINOR_REMOTE_PTZCTRL = 0x81; /* 远程云台控制 */ + public static final int MINOR_REMOTE_FORMAT_HDD = 0x82; /* 远程格式化硬盘 */ + public static final int MINOR_REMOTE_STOP = 0x83; /* 远程关机 */ + public static final int MINOR_REMOTE_LOCKFILE = 0x84;/* 远程锁定文件 */ + public static final int MINOR_REMOTE_UNLOCKFILE = 0x85;/* 远程解锁文件 */ + public static final int MINOR_REMOTE_CFGFILE_OUTPUT = 0x86; /* 远程导出配置文件 */ + public static final int MINOR_REMOTE_CFGFILE_INTPUT = 0x87; /* 远程导入配置文件 */ + public static final int MINOR_REMOTE_RECFILE_OUTPUT = 0x88; /* 远程导出录象文件 */ + public static final int MINOR_REMOTE_DVR_ALARM = 0x89; /* 远程手动清除和触发报警*/ + public static final int MINOR_REMOTE_IPC_ADD = 0x8a; /* 远程添加IPC */ + public static final int MINOR_REMOTE_IPC_DEL = 0x8b;/* 远程删除IPC */ + public static final int MINOR_REMOTE_IPC_SET = 0x8c; /* 远程设置IPC */ + public static final int MINOR_REBOOT_VCA_LIB = 0x8d; /*重启智能库*/ + + /*日志附加信息*/ +//主类型 + public static final int MAJOR_INFORMATION = 0x4; /*附加信息*/ +//次类型 + public static final int MINOR_HDD_INFO = 0xa1;/*硬盘信息*/ + public static final int MINOR_SMART_INFO = 0xa2; /*SMART信息*/ + public static final int MINOR_REC_START = 0xa3; /*开始录像*/ + public static final int MINOR_REC_STOP = 0xa4;/*停止录像*/ + public static final int MINOR_REC_OVERDUE = 0xa5;/*过期录像删除*/ + public static final int MINOR_LINK_START = 0xa6; // ivms多路解码器等连接前端设备 + public static final int MINOR_LINK_STOP = 0xa7;// ivms多路解码器等断开前端设备  +//当日志的主类型为MAJOR_OPERATION=03,次类型为MINOR_LOCAL_CFG_PARM=0x52或者MINOR_REMOTE_GET_PARM=0x76或者MINOR_REMOTE_CFG_PARM=0x77时,dwParaType:参数类型有效,其含义如下: + public static final int PARA_VIDEOOUT = 0x1; + public static final int PARA_IMAGE = 0x2; + public static final int PARA_ENCODE = 0x4; + public static final int PARA_NETWORK = 0x8; + public static final int PARA_ALARM = 0x10; + public static final int PARA_EXCEPTION = 0x20; + public static final int PARA_DECODER = 0x40; /*解码器*/ + public static final int PARA_RS232 = 0x80; + public static final int PARA_PREVIEW = 0x100; + public static final int PARA_SECURITY = 0x200; + public static final int PARA_DATETIME = 0x400; + public static final int PARA_FRAMETYPE = 0x800; /*帧格式*/ + public static final int PARA_VCA_RULE = 0x1000; //行为规则 +//SDK_V222 +//智能设备类型 + public static final int DS6001_HF_B = 60;//行为分析:DS6001-HF/B + public static final int DS6001_HF_P = 61;//车牌识别:DS6001-HF/P + public static final int DS6002_HF_B = 62;//双机跟踪:DS6002-HF/B + public static final int DS6101_HF_B = 63;//行为分析:DS6101-HF/B + public static final int IVMS_2000 = 64;//智能分析仪 + public static final int DS9000_IVS = 65;//9000系列智能DVR + public static final int DS8004_AHL_A = 66;//智能ATM, DS8004AHL-S/A + public static final int DS6101_HF_P = 67;//车牌识别:DS6101-HF/P +//能力获取命令 + public static final int VCA_DEV_ABILITY = 0x100;//设备智能分析的总能力 + public static final int VCA_CHAN_ABILITY = 0x110;//行为分析能力 + public static final int DEVICE_ABILITY_INFO = 0x011; //设备通用能力类型,具体能力根据发送的能力节点来区分 +//获取/设置大接口参数配置命令 +//车牌识别(NET_VCA_PLATE_CFG); + public static final int NET_DVR_SET_PLATECFG = 150 ;//设置车牌识别参数 + + public static final int NET_DVR_GET_PLATECFG = 151; //获取车牌识别参数 +//行为对应(NET_VCA_RULECFG) + public static final int NET_DVR_SET_RULECFG = 152; //设置行为分析规则 + public static final int NET_DVR_GET_RULECFG = 153;//获取行为分析规则, +//双摄像机标定参数(NET_DVR_LF_CFG) + public static final int NET_DVR_SET_LF_CFG = 160;//设置双摄像机的配置参数 + public static final int NET_DVR_GET_LF_CFG = 161;//获取双摄像机的配置参数 +//智能分析仪取流配置结构 + public static final int NET_DVR_SET_IVMS_STREAMCFG = 162; //设置智能分析仪取流参数 + public static final int NET_DVR_GET_IVMS_STREAMCFG = 163; //获取智能分析仪取流参数 +//智能控制参数结构 + public static final int NET_DVR_SET_VCA_CTRLCFG = 164; //设置智能控制参数 + public static final int NET_DVR_GET_VCA_CTRLCFG = 165; //获取智能控制参数 +//屏蔽区域NET_VCA_MASK_REGION_LIST + public static final int NET_DVR_SET_VCA_MASK_REGION = 166; //设置屏蔽区域参数 + public static final int NET_DVR_GET_VCA_MASK_REGION = 167; //获取屏蔽区域参数 +//ATM进入区域 NET_VCA_ENTER_REGION + public static final int NET_DVR_SET_VCA_ENTER_REGION = 168; //设置进入区域参数 + public static final int NET_DVR_GET_VCA_ENTER_REGION = 169; //获取进入区域参数 +//标定线配置NET_VCA_LINE_SEGMENT_LIST + public static final int NET_DVR_SET_VCA_LINE_SEGMENT = 170; //设置标定线 + public static final int NET_DVR_GET_VCA_LINE_SEGMENT = 171; //获取标定线 +// ivms屏蔽区域NET_IVMS_MASK_REGION_LIST + public static final int NET_DVR_SET_IVMS_MASK_REGION = 172; //设置IVMS屏蔽区域参数 + public static final int NET_DVR_GET_IVMS_MASK_REGION = 173; //获取IVMS屏蔽区域参数 +// ivms进入检测区域NET_IVMS_ENTER_REGION + public static final int NET_DVR_SET_IVMS_ENTER_REGION = 174; //设置IVMS进入区域参数 + public static final int NET_DVR_GET_IVMS_ENTER_REGION = 175; //获取IVMS进入区域参数 + public static final int NET_DVR_SET_IVMS_BEHAVIORCFG = 176;//设置智能分析仪行为规则参数 + public static final int NET_DVR_GET_IVMS_BEHAVIORCFG = 177; //获取智能分析仪行为规则参数 + /**********************设备类型 end***********************/ + + /************************************************* + 参数配置结构、参数(其中_V30为9000新增) + **************************************************/ + + ///////////////////////////////////////////////////////////////////////// + //校时结构参数 + public static class NET_DVR_TIME extends Structure {//校时结构参数 + public int dwYear; //年 + public int dwMonth; //月 + public int dwDay; //日 + public int dwHour; //时 + public int dwMinute; //分 + public int dwSecond; //秒 + + public String toString() { + return "NET_DVR_TIME.dwYear: " + dwYear + "\n" + "NET_DVR_TIME.dwMonth: \n" + dwMonth + "\n" + "NET_DVR_TIME.dwDay: \n" + dwDay + "\n" + "NET_DVR_TIME.dwHour: \n" + dwHour + "\n" + "NET_DVR_TIME.dwMinute: \n" + dwMinute + "\n" + "NET_DVR_TIME.dwSecond: \n" + dwSecond; + } + + //用于列表中显示 + public String toStringTime() + { + return String.format("%02d/%02d/%02d%02d:%02d:%02d", dwYear, dwMonth, dwDay, dwHour, dwMinute, dwSecond); + } + + //存储文件名使用 + public String toStringTitle() + { + return String.format("Time%02d%02d%02d%02d%02d%02d", dwYear, dwMonth, dwDay, dwHour, dwMinute, dwSecond); + } + } + + public static class NET_DVR_SCHEDTIME extends Structure { + public byte byStartHour; //开始时间 + public byte byStartMin; + public byte byStopHour; //结束时间 + public byte byStopMin; + + + } + + public static class NET_DVR_HANDLEEXCEPTION_V30 extends Structure { + public int dwHandleType; /*处理方式,处理方式的"或"结果*//*0x00: 无响应*//*0x01: 监视器上警告*//*0x02: 声音警告*//*0x04: 上传中心*/ /*0x08: 触发报警输出*//*0x20: 触发抓图*/ //(JPEG定制) + public byte[] byRelAlarmOut = new byte[MAX_ALARMOUT_V30]; //报警触发的输出通道,报警触发的输出,为1表示触发该输出 + +} + +//报警和异常处理结构(子结构)(多处使用) + public static class NET_DVR_HANDLEEXCEPTION extends Structure { + public int dwHandleType; /*处理方式,处理方式的"或"结果*//*0x00: 无响应*//*0x01: 监视器上警告*//*0x02: 声音警告*//*0x04: 上传中心*/ /*0x08: 触发报警输出*//*0x20: 触发抓图*/ //(JPEG定制) + public byte[] byRelAlarmOut = new byte[MAX_ALARMOUT]; //报警触发的输出通道,报警触发的输出,为1表示触发该输出 + +} + +//DVR设备参数 + public static class NET_DVR_DEVICECFG extends Structure { + public int dwSize; + public byte[] sDVRName = new byte[NAME_LEN]; //DVR名称 + public int dwDVRID; //DVR ID,用于遥控器 //V1.4(0-99), V1.5(0-255) + public int dwRecycleRecord; //是否循环录像,0:不是; 1:是 + //以下不可更改 + public byte[] sSerialNumber = new byte[SERIALNO_LEN]; //序列号 + public int dwSoftwareVersion; //软件版本号,高16位是主版本,低16位是次版本 + public int dwSoftwareBuildDate; //软件生成日期,0xYYYYMMDD + public int dwDSPSoftwareVersion; //DSP软件版本,高16位是主版本,低16位是次版本 + public int dwDSPSoftwareBuildDate; // DSP软件生成日期,0xYYYYMMDD + public int dwPanelVersion; // 前面板版本,高16位是主版本,低16位是次版本 + public int dwHardwareVersion; // 硬件版本,高16位是主版本,低16位是次版本 + public byte byAlarmInPortNum; //DVR报警输入个数 + public byte byAlarmOutPortNum; //DVR报警输出个数 + public byte byRS232Num; //DVR 232串口个数 + public byte byRS485Num; //DVR 485串口个数 + public byte byNetworkPortNum; //网络口个数 + public byte byDiskCtrlNum; //DVR 硬盘控制器个数 + public byte byDiskNum; //DVR 硬盘个数 + public byte byDVRType; //DVR类型, 1:DVR 2:ATM DVR 3:DVS ...... + public byte byChanNum; //DVR 通道个数 + public byte byStartChan; //起始通道号,例如DVS-1,DVR - 1 + public byte byDecordChans; //DVR 解码路数 + public byte byVGANum; //VGA口的个数 + public byte byUSBNum; //USB口的个数 + public byte byAuxoutNum; //辅口的个数 + public byte byAudioNum; //语音口的个数 + public byte byIPChanNum; //最大数字通道数 + + + } + + //DVR设备参数 + public static class NET_DVR_DEVICECFG_V40 extends Structure { + public int dwSize; + public byte[] sDVRName = new byte[NAME_LEN]; //DVR名称 + public int dwDVRID; //DVR ID,用于遥控器 //V1.4(0-99), V1.5(0-255) + public int dwRecycleRecord; //是否循环录像,0:不是; 1:是 + //以下不可更改 + public byte[] sSerialNumber = new byte[SERIALNO_LEN]; //序列号 + public int dwSoftwareVersion; //软件版本号,高16位是主版本,低16位是次版本 + public int dwSoftwareBuildDate; //软件生成日期,0xYYYYMMDD + public int dwDSPSoftwareVersion; //DSP软件版本,高16位是主版本,低16位是次版本 + public int dwDSPSoftwareBuildDate; // DSP软件生成日期,0xYYYYMMDD + public int dwPanelVersion; // 前面板版本,高16位是主版本,低16位是次版本 + public int dwHardwareVersion; // 硬件版本,高16位是主版本,低16位是次版本 + public byte byAlarmInPortNum; //DVR报警输入个数 + public byte byAlarmOutPortNum; //DVR报警输出个数 + public byte byRS232Num; //DVR 232串口个数 + public byte byRS485Num; //DVR 485串口个数 + public byte byNetworkPortNum; //网络口个数 + public byte byDiskCtrlNum; //DVR 硬盘控制器个数 + public byte byDiskNum; //DVR 硬盘个数 + public byte byDVRType; //DVR类型, 1:DVR 2:ATM DVR 3:DVS ...... + public byte byChanNum; //DVR 通道个数 + public byte byStartChan; //起始通道号,例如DVS-1,DVR - 1 + public byte byDecordChans; //DVR 解码路数 + public byte byVGANum; //VGA口的个数 + public byte byUSBNum; //USB口的个数 + public byte byAuxoutNum; //辅口的个数 + public byte byAudioNum; //语音口的个数 + public byte byIPChanNum; //最大数字通道数 低8位,高8位见byHighIPChanNum + public byte byZeroChanNum; //零通道编码个数 + public byte bySupport; //能力,位与结果为0表示不支持,1表示支持, + public byte byEsataUseage; //Esata的默认用途,0-默认备份,1-默认录像 + public byte byIPCPlug; //0-关闭即插即用,1-打开即插即用 + public byte byStorageMode; //0-盘组模式,1-磁盘配额, 2抽帧模式, 3-自动 + public byte bySupport1; //能力,位与结果为0表示不支持,1表示支持 + public short wDevType;//设备型号 + public byte[] byDevTypeName = new byte[DEV_TYPE_NAME_LEN];//设备型号名称 + public byte bySupport2; //能力集扩展,位与结果为0表示不支持,1表示支持 + //bySupport2 & 0x1, 表示是否支持扩展的OSD字符叠加(终端和抓拍机扩展区分) + public byte byAnalogAlarmInPortNum; //模拟报警输入个数 + public byte byStartAlarmInNo; //模拟报警输入起始号 + public byte byStartAlarmOutNo; //模拟报警输出起始号 + public byte byStartIPAlarmInNo; //IP报警输入起始号 + public byte byStartIPAlarmOutNo; //IP报警输出起始号 + public byte byHighIPChanNum; //数字通道个数,高8位 + public byte byEnableRemotePowerOn;//是否启用在设备休眠的状态下远程开机功能,0-不启用,1-启用 + public short wDevClass; //设备大类备是属于哪个产品线,0 保留,1-50 DVR,51-100 DVS,101-150 NVR,151-200 IPC,65534 其他,具体分类方法见《设备类型对应序列号和类型值.docx》 + public byte[] byRes2 = new byte[6]; //保留 + + +} + +public static class NET_DVR_IPADDR extends Structure { + public byte[] sIpV4 = new byte[16]; + public byte[] byRes = new byte[128]; + + public String toString() { + return "NET_DVR_IPADDR.sIpV4: " + new String(sIpV4) + "\n" + "NET_DVR_IPADDR.byRes: " + new String(byRes) + "\n"; + } + + + } + + +//网络数据结构(子结构)(9000扩展) + public static class NET_DVR_ETHERNET_V30 extends Structure { + public NET_DVR_IPADDR struDVRIP; + public NET_DVR_IPADDR struDVRIPMask; + public int dwNetInterface; + public short wDVRPort; + public short wMTU; + public byte[] byMACAddr = new byte[6]; + + public String toString() { + return "NET_DVR_ETHERNET_V30.struDVRIP: \n" + struDVRIP + "\n" + "NET_DVR_ETHERNET_V30.struDVRIPMask: \n" + struDVRIPMask + "\n" + "NET_DVR_ETHERNET_V30.dwNetInterface: " + dwNetInterface + "\n" + "NET_DVR_ETHERNET_V30.wDVRPort: " + wDVRPort + "\n" + "NET_DVR_ETHERNET_V30.wMTU: " + wMTU + "\n" + "NET_DVR_ETHERNET_V30.byMACAddr: " + new String(byMACAddr) + "\n"; + } + + + } + +public static class NET_DVR_ETHERNET extends Structure {//网络数据结构(子结构) + public byte[] sDVRIP = new byte[16]; //DVR IP地址 + public byte[] sDVRIPMask = new byte[16]; //DVR IP地址掩码 + public int dwNetInterface; //网络接口 1-10MBase-T 2-10MBase-T全双工 3-100MBase-TX 4-100M全双工 5-10M/100M自适应 + public short wDVRPort; //端口号 + public byte[] byMACAddr = new byte[MACADDR_LEN]; //服务器的物理地址 + + +} + + public static class NET_DVR_PPPOECFG extends Structure {//PPPoe + public int dwPPPoE; + public byte[] sPPPoEUser = new byte[32]; + public byte[] sPPPoEPassword = new byte[16]; + public NET_DVR_IPADDR struPPPoEIP; + + + } + + public static class NET_DVR_NETCFG_V30 extends Structure { + public int dwSize; + public NET_DVR_ETHERNET_V30[] struEtherNet = new NET_DVR_ETHERNET_V30[2]; + public NET_DVR_IPADDR[] struRes1 = new NET_DVR_IPADDR[2]; + public NET_DVR_IPADDR struAlarmHostIpAddr; + public short[] wRes2 = new short[2]; + public short wAlarmHostIpPort; + public byte byUseDhcp; + public byte byRes3; + public NET_DVR_IPADDR struDnsServer1IpAddr; + public NET_DVR_IPADDR struDnsServer2IpAddr; + public byte[] byIpResolver = new byte[64]; + public short wIpResolverPort; + public short wHttpPortNo; + public NET_DVR_IPADDR struMulticastIpAddr; + public NET_DVR_IPADDR struGatewayIpAddr; + public NET_DVR_PPPOECFG struPPPoE; + public byte[] byRes = new byte[64]; + + public String toString() { + return "NET_DVR_NETCFG_V30.dwSize: " + dwSize + "\n" + "NET_DVR_NETCFG_V30.struEtherNet[0]: \n" + struEtherNet[0] + "\n" + "NET_DVR_NETCFG_V30.struAlarmHostIpAddr: \n" + struAlarmHostIpAddr + "\n" + "NET_DVR_NETCFG_V30.wAlarmHostIpPort: " + wAlarmHostIpPort + "\n" + "NET_DVR_NETCFG_V30.wHttpPortNo: " + wHttpPortNo + "\n" + "NET_DVR_NETCFG_V30.struGatewayIpAddr: \n" + struGatewayIpAddr + "\n"; + } + + + } + + + public static class NET_DVR_NETCFG extends Structure {//网络配置结构 + public int dwSize; + public NET_DVR_ETHERNET[] struEtherNet = new NET_DVR_ETHERNET[MAX_ETHERNET]; /* 以太网口 */ + public byte[] sManageHostIP = new byte[16]; //远程管理主机地址 + public short wManageHostPort; //远程管理主机端口号 + public byte[] sIPServerIP = new byte[16]; //IPServer服务器地址 + public byte[] sMultiCastIP = new byte[16]; //多播组地址 + public byte[] sGatewayIP = new byte[16]; //网关地址 + public byte[] sNFSIP = new byte[16]; //NFS主机IP地址 + public byte[] sNFSDirectory = new byte[PATHNAME_LEN];//NFS目录 + public int dwPPPOE; //0-不启用,1-启用 + public byte[] sPPPoEUser = new byte[NAME_LEN]; //PPPoE用户名 + public byte[] sPPPoEPassword = new byte[PASSWD_LEN];// PPPoE密码 + public byte[] sPPPoEIP = new byte[16]; //PPPoE IP地址(只读) + + +} + +//通道图象结构 + public static class NET_DVR_SCHEDTIMEWEEK extends Structure { + public NET_DVR_SCHEDTIME[] struAlarmTime = new NET_DVR_SCHEDTIME[8]; + } + + public static class byte96 extends Structure { + public byte[] byMotionScope = new byte[96]; + } + + public static class NET_DVR_MOTION_V30 extends Structure {//移动侦测(子结构)(9000扩展) + public byte96[] byMotionScope = new byte96[64]; /*侦测区域,0-96位,表示64行,共有96*64个小宏块,为1表示是移动侦测区域,0-表示不是*/ + public byte byMotionSensitive; /*移动侦测灵敏度, 0 - 5,越高越灵敏,oxff关闭*/ + public byte byEnableHandleMotion; /* 是否处理移动侦测 0-否 1-是*/ + public byte byPrecision; /* 移动侦测算法的进度: 0--16*16, 1--32*32, 2--64*64 ... */ + public byte reservedData; + public NET_DVR_HANDLEEXCEPTION_V30 struMotionHandleType; /* 处理方式 */ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS]; /*布防时间*/ + public byte[] byRelRecordChan = new byte[64]; /* 报警触发的录象通道*/ + } + + public static class NET_DVR_MOTION extends Structure {//移动侦测(子结构) + public byte[] byMotionScope = new byte[18*22]; /*侦测区域,共有22*18个小宏块,为1表示改宏块是移动侦测区域,0-表示不是*/ + public byte byMotionSensitive; /*移动侦测灵敏度, 0 - 5,越高越灵敏,0xff关闭*/ + public byte byEnableHandleMotion; /* 是否处理移动侦测 */ + public byte[] reservedData = new byte[2]; + public NET_DVR_HANDLEEXCEPTION strMotionHandleType; /* 处理方式 */ + public byte[] byRelRecordChan = new byte[MAX_CHANNUM]; //报警触发的录象通道,为1表示触发该通道 + } + + public static class NET_DVR_HIDEALARM_V30 extends Structure {//遮挡报警 + public int dwEnableHideAlarm; /* 是否启动遮挡报警 ,0-否,1-低灵敏度 2-中灵敏度 3-高灵敏度*/ + public short wHideAlarmAreaTopLeftX; /* 遮挡区域的x坐标 */ + public short wHideAlarmAreaTopLeftY; /* 遮挡区域的y坐标 */ + public short wHideAlarmAreaWidth; /* 遮挡区域的宽 */ + public short wHideAlarmAreaHeight; /*遮挡区域的高*/ + public NET_DVR_HANDLEEXCEPTION_V30 strHideAlarmHandleType; /* 处理方式 */ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];//布防时间 + } + + public static class NET_DVR_HIDEALARM extends Structure {//遮挡报警(子结构) 区域大小704*576 + public int dwEnableHideAlarm; /* 是否启动遮挡报警 ,0-否,1-低灵敏度 2-中灵敏度 3-高灵敏度*/ + public short wHideAlarmAreaTopLeftX; /* 遮挡区域的x坐标 */ + public short wHideAlarmAreaTopLeftY; /* 遮挡区域的y坐标 */ + public short wHideAlarmAreaWidth; /* 遮挡区域的宽 */ + public short wHideAlarmAreaHeight; /*遮挡区域的高*/ + public NET_DVR_HANDLEEXCEPTION strHideAlarmHandleType; /* 处理方式 */ + } + + public static class NET_DVR_VILOST_V30 extends Structure { //信号丢失报警(子结构)(9000扩展) + public byte byEnableHandleVILost; /* 是否处理信号丢失报警 */ + public NET_DVR_HANDLEEXCEPTION_V30 strVILostHandleType; /* 处理方式 */ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];//布防时间 + } + +public static class NET_DVR_VILOST extends Structure { //信号丢失报警(子结构) + public byte byEnableHandleVILost; /* 是否处理信号丢失报警 */ + public NET_DVR_HANDLEEXCEPTION strVILostHandleType; /* 处理方式 */ +} + + public static class NET_DVR_SHELTER extends Structure { //遮挡区域(子结构) + public short wHideAreaTopLeftX; /* 遮挡区域的x坐标 */ + public short wHideAreaTopLeftY; /* 遮挡区域的y坐标 */ + public short wHideAreaWidth; /* 遮挡区域的宽 */ + public short wHideAreaHeight; /* 遮挡区域的高*/ + } + + public static class NET_DVR_COLOR extends Structure { + public byte byBrightness; /*亮度,0-255*/ + public byte byContrast; /*对比度,0-255*/ + public byte bySaturation; /*饱和度,0-255*/ + public byte byHue; /*色调,0-255*/ + } + + public static class NET_DVR_VICOLOR extends Structure { + public NET_DVR_COLOR[] struColor = new NET_DVR_COLOR[MAX_TIMESEGMENT_V30];/*图象参数(第一个有效,其他三个保留)*/ + public NET_DVR_SCHEDTIME[] struHandleTime = new NET_DVR_SCHEDTIME[MAX_TIMESEGMENT_V30];/*处理时间段(保留)*/ + } + + //信号丢失 + public static class NET_DVR_VILOST_V40 extends Structure { + public int dwEnableVILostAlarm; /* 是否启动信号丢失报警 ,0-否,1-是*/ + /* 信号丢失触发报警输出 */ + public int dwHandleType; //异常处理,异常处理方式的"或"结果 + /*0x00: 无响应*/ + /*0x01: 监视器上警告*/ + /*0x02: 声音警告*/ + /*0x04: 上传中心*/ + /*0x08: 触发报警输出*/ + /*0x10: 触发JPRG抓图并上传Email*/ + /*0x20: 无线声光报警器联动*/ + /*0x40: 联动电子地图(目前只有PCNVR支持)*/ + /*0x200: 抓图并上传FTP*/ + /*0x1000:抓图上传到云*/ + public int dwMaxRelAlarmOutChanNum ; //触发的报警输出通道数(只读)最大支持数量 + public int[] dwRelAlarmOut = new int[MAX_ALARMOUT_V40]; /*触发报警输出号,按值表示,采用紧凑型排列,从下标0 - dwRelAlarmOut -1有效,如果中间遇到0xffffffff,则后续无效*/ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS]; /*布防时间*/ + public byte byVILostAlarmThreshold; /*信号丢失报警阈值,当值低于阈值,认为信号丢失,取值0-99*/ + public byte[] byRes = new byte[63]; //保留 + + + } + + public static class NET_DVR_DNMODE extends Structure { + public byte byObjectSize;//占比参数(0~100) + public byte byMotionSensitive; /*移动侦测灵敏度, 0 - 5,越高越灵敏,0xff关闭*/ + public byte[] byRes = new byte[6]; + + + } + + public static class NET_DVR_MOTION_MULTI_AREAPARAM extends Structure { + public byte byAreaNo;//区域编号(IPC- 1~8) + public byte[] byRes = new byte[3]; + public NET_VCA_RECT struRect = new NET_VCA_RECT();//单个区域的坐标信息(矩形) size = 16; + public NET_DVR_DNMODE struDayNightDisable = new NET_DVR_DNMODE();//关闭模式 + public NET_DVR_DNMODE struDayModeParam = new NET_DVR_DNMODE();//白天模式 + public NET_DVR_DNMODE struNightModeParam = new NET_DVR_DNMODE();//夜晚模式 + public byte[] byRes1 = new byte[8]; + + + } + + public static final int MAX_MULTI_AREA_NUM = 24; + public static class NET_DVR_MOTION_MULTI_AREA extends Structure { + public byte byDayNightCtrl;//日夜控制 0~关闭,1~自动切换,2~定时切换(默认关闭) + public byte byAllMotionSensitive; /*移动侦测灵敏度, 0 - 5,越高越灵敏,0xff关闭,全部区域的灵敏度范围*/ + public byte[] byRes = new byte[2];// + public NET_DVR_SCHEDULE_DAYTIME struScheduleTime = new NET_DVR_SCHEDULE_DAYTIME();//切换时间 16 + public NET_DVR_MOTION_MULTI_AREAPARAM[] struMotionMultiAreaParam = new NET_DVR_MOTION_MULTI_AREAPARAM[MAX_MULTI_AREA_NUM];//最大支持24个区域 + public byte[] byRes1 = new byte[60]; + + + } + + public static class NET_DVR_MOTION_SINGLE_AREA extends Structure { + public byte[] byMotionScope = new byte[64*96]; /*侦测区域,0-96位,表示64行,共有96*64个小宏块,目前有效的是22*18,为1表示是移动侦测区域,0-表示不是*/ + public byte byMotionSensitive; /*移动侦测灵敏度, 0 - 5,越高越灵敏,0xff关闭*/ + public byte[] byRes = new byte[3]; + + + } + + public static class NET_DVR_MOTION_MODE_PARAM extends Structure { + public NET_DVR_MOTION_SINGLE_AREA struMotionSingleArea = new NET_DVR_MOTION_SINGLE_AREA(); //普通模式下的单区域设 + public NET_DVR_MOTION_MULTI_AREA struMotionMultiArea = new NET_DVR_MOTION_MULTI_AREA(); //专家模式下的多区域设置 + + + } + + public static class NET_DVR_MOTION_V40 extends Structure { + public NET_DVR_MOTION_MODE_PARAM struMotionMode = new NET_DVR_MOTION_MODE_PARAM(); //(5.1.0新增) + public byte byEnableHandleMotion; /* 是否处理移动侦测 0-否 1-是*/ + public byte byEnableDisplay; /*启用移动侦测高亮显示,0-否,1-是*/ + public byte byConfigurationMode; //0~普通,1~专家(5.1.0新增) + public byte byKeyingEnable; //启用键控移动侦测 0-不启用,1-启用 + /* 异常处理方式 */ + public int dwHandleType; //异常处理,异常处理方式的"或"结果 + /*0x00: 无响应*/ + /*0x01: 监视器上警告*/ + /*0x02: 声音警告*/ + /*0x04: 上传中心*/ + /*0x08: 触发报警输出*/ + /*0x10: 触发JPRG抓图并上传Email*/ + /*0x20: 无线声光报警器联动*/ + /*0x40: 联动电子地图(目前只有PCNVR支持)*/ + /*0x200: 抓图并上传FTP*/ + /*0x1000: 抓图上传到云*/ + public int dwMaxRelAlarmOutChanNum ; //触发的报警输出通道数(只读)最大支持数量 + public int[] dwRelAlarmOut = new int[MAX_ALARMOUT_V40]; //实际触发的报警输出号,按值表示,采用紧凑型排列,从下标0 - dwRelAlarmOut -1有效,如果中间遇到0xffffffff,则后续无效 + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS]; /*布防时间*/ + /*触发的录像通道*/ + public int dwMaxRecordChanNum; //设备支持的最大关联录像通道数-只读 + public int[] dwRelRecordChan = new int[MAX_CHANNUM_V40]; /* 实际触发录像通道,按值表示,采用紧凑型排列,从下标0 - dwRelRecordChan -1有效,如果中间遇到0xffffffff,则后续无效*/ + public byte byDiscardFalseAlarm; //启用去误报 0-无效,1-不启用,2-启用 + public byte[] byRes = new byte[127]; //保留字节 + + + } + + public static class NET_DVR_RGB_COLOR extends Structure { + public byte byRed; //RGB颜色三分量中的红色 + public byte byGreen; //RGB颜色三分量中的绿色 + public byte byBlue; //RGB颜色三分量中的蓝色 + public byte byRes; //保留 + + + } + + public static class NET_DVR_HIDEALARM_V40 extends Structure { + public int dwEnableHideAlarm; /* 是否启动遮挡报警,0-否,1-低灵敏度,2-中灵敏度,3-高灵敏度*/ + public short wHideAlarmAreaTopLeftX; /* 遮挡区域的x坐标 */ + public short wHideAlarmAreaTopLeftY; /* 遮挡区域的y坐标 */ + public short wHideAlarmAreaWidth; /* 遮挡区域的宽 */ + public short wHideAlarmAreaHeight; /*遮挡区域的高*/ + /* 信号丢失触发报警输出 */ + public int dwHandleType; //异常处理,异常处理方式的"或"结果 + /*0x00: 无响应*/ + /*0x01: 监视器上警告*/ + /*0x02: 声音警告*/ + /*0x04: 上传中心*/ + /*0x08: 触发报警输出*/ + /*0x10: 触发JPRG抓图并上传Email*/ + /*0x20: 无线声光报警器联动*/ + /*0x40: 联动电子地图(目前只有PCNVR支持)*/ + /*0x200: 抓图并上传FTP*/ + /*0x1000:抓图上传到云*/ + public int dwMaxRelAlarmOutChanNum ; //触发的报警输出通道数(只读)最大支持数量 + public int[] dwRelAlarmOut = new int[MAX_ALARMOUT_V40]; /*触发报警输出号,按值表示,采用紧凑型排列,从下标0 - dwRelAlarmOut -1有效,如果中间遇到0xffffffff,则后续无效*/ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS]; /*布防时间*/ + public byte[] byRes = new byte[64]; //保留 + + + }//遮挡报警 + + public static class NET_DVR_PICCFG_V40 extends Structure { + public int dwSize; + public byte[] sChanName = new byte[NAME_LEN]; + public int dwVideoFormat; /* 只读 视频制式 1-NTSC 2-PAL */ + public NET_DVR_VICOLOR struViColor = new NET_DVR_VICOLOR();// 图像参数按时间段设置 + //显示通道名 + public int dwShowChanName; // 预览的图象上是否显示通道名称,0-不显示,1-显示 + public short wShowNameTopLeftX; /* 通道名称显示位置的x坐标 */ + public short wShowNameTopLeftY; /* 通道名称显示位置的y坐标 */ + //隐私遮挡 + public int dwEnableHide; /* 是否启动遮挡 ,0-否,1-是*/ + public NET_DVR_SHELTER[] struShelter = new NET_DVR_SHELTER[MAX_SHELTERNUM]; + //OSD + public int dwShowOsd;// 预览的图象上是否显示OSD,0-不显示,1-显示 + public short wOSDTopLeftX; /* OSD的x坐标 */ + public short wOSDTopLeftY; /* OSD的y坐标 */ + public byte byOSDType; /* OSD类型(主要是年月日格式) */ + /* 0: XXXX-XX-XX 年月日 */ + /* 1: XX-XX-XXXX 月日年 */ + /* 2: XXXX年XX月XX日 */ + /* 3: XX月XX日XXXX年 */ + /* 4: XX-XX-XXXX 日月年*/ + /* 5: XX日XX月XXXX年 */ + /*6: xx/xx/xxxx(月/日/年) */ + /*7: xxxx/xx/xx(年/月/日) */ + /*8: xx/xx/xxxx(日/月/年)*/ + public byte byDispWeek; /* 是否显示星期 */ + public byte byOSDAttrib; /* OSD属性:透明,闪烁 */ + /* 0: 不显示OSD */ + /* 1: 透明,闪烁 */ + /* 2: 透明,不闪烁 */ + /* 3: 不透明,闪烁 */ + /* 4: 不透明,不闪烁 */ + public byte byHourOSDType; /* OSD小时制:0-24小时制,1-12小时制 */ + public byte byFontSize; //16*16(中)/8*16(英),1-32*32(中)/16*32(英),2-64*64(中)/32*64(英) 3-48*48(中)/24*48(英) 4-24*24(中)/12*24(英) 5-96*96(中)/48*96(英) 6-128*128(中)/64*128(英) 7-80*80(中)/40*80(英) 8-112*112(中)/56*112(英) 0xff-自适应(adaptive) + public byte byOSDColorType; //0-默认(黑白);1-自定义 + public byte byAlignment;//对齐方式 0-自适应,1-右对齐, 2-左对齐,3-国标模式,4-全部右对齐(包含叠加字符、时间以及标题等所有OSD字符),5-全部左对齐(包含叠加字符、时间以及标题等所有OSD字符) + public byte byOSDMilliSecondEnable;//视频叠加时间支持毫秒;0~不叠加, 1-叠加 + public NET_DVR_VILOST_V40 struVILost = new NET_DVR_VILOST_V40(); //视频信号丢失报警(支持组) + public NET_DVR_VILOST_V40 struAULost = new NET_DVR_VILOST_V40(); /*音频信号丢失报警(支持组)*/ + public NET_DVR_MOTION_V40 struMotion = new NET_DVR_MOTION_V40(); //移动侦测报警(支持组) + public NET_DVR_HIDEALARM_V40 struHideAlarm = new NET_DVR_HIDEALARM_V40(); //遮挡报警(支持组) + public NET_DVR_RGB_COLOR struOsdColor = new NET_DVR_RGB_COLOR();//OSD颜色 + public int dwBoundary; //边界值,左对齐,右对齐以及国标模式的边界值,0-表示默认值,单位:像素;在国标模式下,单位修改为字符个数(范围是,0,1,2) + public NET_DVR_RGB_COLOR struOsdBkColor = new NET_DVR_RGB_COLOR(); //自定义OSD背景色 + public byte byOSDBkColorMode; //OSD背景色模式,0-默认,1-自定义OSD背景色 + public byte byUpDownBoundary; //上下最小边界值选项,单位为字符个数(范围是,0,1,2),国标模式下无效。byAlignment=3该字段无效,通过dwBoundary进行边界配置,.byAlignment不等于3的情况下, byUpDownBoundary/byLeftRightBoundary配置成功后,dwBoundary值将不生效 + public byte byLeftRightBoundary; //左右最小边界值选项,单位为字符个数(范围是,0,1,2), 国标模式下无效。byAlignment=3该字段无效,通过dwBoundary进行边界配置,.byAlignment不等于3的情况下, byUpDownBoundary/byLeftRightBoundary配置成功后,dwBoundary值将不生效 + public byte[] byRes = new byte[113]; + + + } + + public static class NET_DVR_PICCFG_V30 extends Structure { + public int dwSize; + public byte[] sChanName = new byte[NAME_LEN]; + public int dwVideoFormat; /* 只读 视频制式 1-NTSC 2-PAL*/ + public NET_DVR_VICOLOR struViColor; // 图像参数按时间段设置 + public int dwShowChanName; // 预览的图象上是否显示通道名称,0-不显示,1-显示 区域大小704*576 + public short wShowNameTopLeftX; /* 通道名称显示位置的x坐标 */ + public short wShowNameTopLeftY; /* 通道名称显示位置的y坐标 */ + public NET_DVR_VILOST_V30 struVILost; //视频信号丢失报警 + public NET_DVR_VILOST_V30 struAULost; /*音频信号丢失报警(保留)*/ + public NET_DVR_MOTION_V30 struMotion; //移动侦测 + public NET_DVR_HIDEALARM_V30 struHideAlarm;//遮挡报警 + public int dwEnableHide; /* 是否启动遮盖(区域大小704*576) ,0-否,1-是*/ + public NET_DVR_SHELTER[] struShelter = new NET_DVR_SHELTER[4]; + public int dwShowOsd; //预览的图象上是否显示OSD,0-不显示,1-显示 区域大小704*576 + public short wOSDTopLeftX; /* OSD的x坐标 */ + public short wOSDTopLeftY; /* OSD的y坐标 */ + public byte byOSDType; /* OSD类型(主要是年月日格式) */ + public byte byDispWeek; /* 是否显示星期 */ + public byte byOSDAttrib; /* OSD属性:透明,闪烁 */ + public byte byHourOSDType; /* OSD小时制:0-24小时制,1-12小时制 */ + public byte[] byRes = new byte[64]; + + + } + + public static class NET_DVR_PICCFG_EX extends Structure {//通道图象结构SDK_V14扩展 + public int dwSize; + public byte[] sChanName = new byte[NAME_LEN]; + public int dwVideoFormat; /* 只读 视频制式 1-NTSC 2-PAL*/ + public byte byBrightness; /*亮度,0-255*/ + public byte byContrast; /*对比度,0-255*/ + public byte bySaturation; /*饱和度,0-255 */ + public byte byHue; /*色调,0-255*/ + //显示通道名 + public int dwShowChanName; // 预览的图象上是否显示通道名称,0-不显示,1-显示 区域大小704*576 + public short wShowNameTopLeftX; /* 通道名称显示位置的x坐标 */ + public short wShowNameTopLeftY; /* 通道名称显示位置的y坐标 */ + //信号丢失报警 + public NET_DVR_VILOST struVILost; + //移动侦测 + public NET_DVR_MOTION struMotion; + //遮挡报警 + public NET_DVR_HIDEALARM struHideAlarm; + //遮挡 区域大小704*576 + public int dwEnableHide; /* 是否启动遮挡 ,0-否,1-是*/ + public NET_DVR_SHELTER[] struShelter = new NET_DVR_SHELTER[MAX_SHELTERNUM]; + //OSD + public int dwShowOsd;// 预览的图象上是否显示OSD,0-不显示,1-显示 区域大小704*576 + public short wOSDTopLeftX; /* OSD的x坐标 */ + public short wOSDTopLeftY; /* OSD的y坐标 */ + public byte byOSDType; /* OSD类型(主要是年月日格式) */ + /* 0: XXXX-XX-XX 年月日 */ + /* 1: XX-XX-XXXX 月日年 */ + /* 2: XXXX年XX月XX日 */ + /* 3: XX月XX日XXXX年 */ + /* 4: XX-XX-XXXX 日月年*/ + /* 5: XX日XX月XXXX年 */ + public byte byDispWeek; /* 是否显示星期 */ + public byte byOSDAttrib; /* OSD属性:透明,闪烁 */ + /* 0: 不显示OSD */ + /* 1: 透明,闪烁 */ + /* 2: 透明,不闪烁 */ + /* 3: 闪烁,不透明 */ + /* 4: 不透明,不闪烁 */ + public byte byHourOsdType; //小时制:0表示24小时制,1-12小时制或am/pm + + +} + + + public static class NET_DVR_PICCFG extends Structure { //通道图象结构(SDK_V13及之前版本) + public int dwSize; + public byte[] sChanName = new byte[NAME_LEN]; + public int dwVideoFormat; /* 只读 视频制式 1-NTSC 2-PAL*/ + public byte byBrightness; /*亮度,0-255*/ + public byte byContrast; /*对比度,0-255*/ + public byte bySaturation; /*饱和度,0-255 */ + public byte byHue; /*色调,0-255*/ + //显示通道名 + public int dwShowChanName; // 预览的图象上是否显示通道名称,0-不显示,1-显示 区域大小704*576 + public short wShowNameTopLeftX; /* 通道名称显示位置的x坐标 */ + public short wShowNameTopLeftY; /* 通道名称显示位置的y坐标 */ + //信号丢失报警 + public NET_DVR_VILOST struVILost; + //移动侦测 + public NET_DVR_MOTION struMotion; + //遮挡报警 + public NET_DVR_HIDEALARM struHideAlarm; + //遮挡 区域大小704*576 + public int dwEnableHide; /* 是否启动遮挡 ,0-否,1-是*/ + public short wHideAreaTopLeftX; /* 遮挡区域的x坐标 */ + public short wHideAreaTopLeftY; /* 遮挡区域的y坐标 */ + public short wHideAreaWidth; /* 遮挡区域的宽 */ + public short wHideAreaHeight; /*遮挡区域的高*/ + //OSD + public int dwShowOsd;// 预览的图象上是否显示OSD,0-不显示,1-显示 区域大小704*576 + public short wOSDTopLeftX; /* OSD的x坐标 */ + public short wOSDTopLeftY; /* OSD的y坐标 */ + public byte byOSDType; /* OSD类型(主要是年月日格式) */ + /* 0: XXXX-XX-XX 年月日 */ + /* 1: XX-XX-XXXX 月日年 */ + /* 2: XXXX年XX月XX日 */ + /* 3: XX月XX日XXXX年 */ + /* 4: XX-XX-XXXX 日月年*/ + /* 5: XX日XX月XXXX年 */ + byte byDispWeek; /* 是否显示星期 */ + byte byOSDAttrib; /* OSD属性:透明,闪烁 */ + /* 0: 不显示OSD */ + /* 1: 透明,闪烁 */ + /* 2: 透明,不闪烁 */ + /* 3: 闪烁,不透明 */ + /* 4: 不透明,不闪烁 */ + public byte reservedData2; + + +} + + public static class NET_DVR_MULTI_STREAM_COMPRESSIONCFG_COND extends Structure{ + public int dwSize; + public NET_DVR_STREAM_INFO struStreamInfo = new NET_DVR_STREAM_INFO(); + public int dwStreamType; + public byte[] byRes = new byte[32]; + } + + public static class NET_DVR_MULTI_STREAM_COMPRESSIONCFG extends Structure{ + public int dwSize; + public int dwStreamType; + public NET_DVR_COMPRESSION_INFO_V30 struStreamPara = new NET_DVR_COMPRESSION_INFO_V30(); + public byte[] byRes = new byte[80]; + } + + //码流压缩参数(子结构)(9000扩展) + public static class NET_DVR_COMPRESSION_INFO_V30 extends Structure { + public byte byStreamType; //码流类型 0-视频流, 1-复合流 + public byte byResolution; //分辨率0-DCIF 1-CIF, 2-QCIF, 3-4CIF, 4-2CIF 5(保留)16-VGA(640*480) 17-UXGA(1600*1200) 18-SVGA (800*600)19-HD720p(1280*720)20-XVGA 21-HD900p + public byte byBitrateType; //码率类型 0:定码率,1:变码率 + public byte byPicQuality; //图象质量 0-最好 1-次好 2-较好 3-一般 4-较差 5-差 + public int dwVideoBitrate; //视频码率 0-保留 1-16K 2-32K 3-48k 4-64K 5-80K 6-96K 7-128K 8-160k 9-192K 10-224K 11-256K 12-320K 13-384K 14-448K 15-512K 16-640K 17-768K 18-896K 19-1024K 20-1280K 21-1536K 22-1792K 23-2048最高位(31位)置成1表示是自定义码流, 0-30位表示码流值。 + public int dwVideoFrameRate; //帧率 0-全部; 1-1/16; 2-1/8; 3-1/4; 4-1/2; 5-1; 6-2; 7-4; 8-6; 9-8; 10-10; 11-12; 12-16; 13-20; V2.0版本中新加14-15; 15-18; 16-22; + public short wIntervalFrameI; //I帧间隔 + public byte byIntervalBPFrame;//0-BBP帧; 1-BP帧; 2-单P帧 + public byte byENumber; //E帧数量(保留) + public byte byVideoEncType;//视频编码类型 0 hik264;1标准h264; 2标准mpeg4; + public byte byAudioEncType;//音频编码类型 0 G722 + public byte[] byres = new byte[10]; + + + } + + //通道压缩参数(9000扩展) + public static class NET_DVR_COMPRESSIONCFG_V30 extends Structure { + public int dwSize; + public NET_DVR_COMPRESSION_INFO_V30 struNormHighRecordPara; //录像 对应8000的普通 + public NET_DVR_COMPRESSION_INFO_V30 struRes; //保留 String[28]; + public NET_DVR_COMPRESSION_INFO_V30 struEventRecordPara; //事件触发压缩参数 + public NET_DVR_COMPRESSION_INFO_V30 struNetPara; //网传(子码流) + + + } + + + public static class NET_DVR_COMPRESSION_INFO extends Structure {//码流压缩参数(子结构) + public byte byStreamType; //码流类型0-视频流,1-复合流,表示压缩参数时最高位表示是否启用压缩参数 + public byte byResolution; //分辨率0-DCIF 1-CIF, 2-QCIF, 3-4CIF, 4-2CIF, 5-2QCIF(352X144)(车载专用) + public byte byBitrateType; //码率类型0:变码率,1:定码率 + public byte byPicQuality; //图象质量 0-最好 1-次好 2-较好 3-一般 4-较差 5-差 + public int dwVideoBitrate; //视频码率 0-保留 1-16K(保留) 2-32K 3-48k 4-64K 5-80K 6-96K 7-128K 8-160k 9-192K 10-224K 11-256K 12-320K + // 13-384K 14-448K 15-512K 16-640K 17-768K 18-896K 19-1024K 20-1280K 21-1536K 22-1792K 23-2048K + //最高位(31位)置成1表示是自定义码流, 0-30位表示码流值(MIN-32K MAX-8192K)。 + public int dwVideoFrameRate; //帧率 0-全部; 1-1/16; 2-1/8; 3-1/4; 4-1/2; 5-1; 6-2; 7-4; 8-6; 9-8; 10-10; 11-12; 12-16; 13-20; + + +} + +public static class NET_DVR_COMPRESSIONCFG extends Structure {//通道压缩参数 + public int dwSize; + public NET_DVR_COMPRESSION_INFO struRecordPara; //录像/事件触发录像 + public NET_DVR_COMPRESSION_INFO struNetPara; //网传/保留 + + +} + + +public static class NET_DVR_COMPRESSION_INFO_EX extends Structure {//码流压缩参数(子结构)(扩展) 增加I帧间隔 + public byte byStreamType; //码流类型0-视频流, 1-复合流 + public byte byResolution; //分辨率0-DCIF 1-CIF, 2-QCIF, 3-4CIF, 4-2CIF, 5-2QCIF(352X144)(车载专用) + public byte byBitrateType; //码率类型0:变码率,1:定码率 + public byte byPicQuality; //图象质量 0-最好 1-次好 2-较好 3-一般 4-较差 5-差 + public int dwVideoBitrate; //视频码率 0-保留 1-16K(保留) 2-32K 3-48k 4-64K 5-80K 6-96K 7-128K 8-160k 9-192K 10-224K 11-256K 12-320K + // 13-384K 14-448K 15-512K 16-640K 17-768K 18-896K 19-1024K 20-1280K 21-1536K 22-1792K 23-2048K + //最高位(31位)置成1表示是自定义码流, 0-30位表示码流值(MIN-32K MAX-8192K)。 + public int dwVideoFrameRate; //帧率 0-全部; 1-1/16; 2-1/8; 3-1/4; 4-1/2; 5-1; 6-2; 7-4; 8-6; 9-8; 10-10; 11-12; 12-16; 13-20, //V2.0增加14-15, 15-18, 16-22; + public short wIntervalFrameI; //I帧间隔 + //2006-08-11 增加单P帧的配置接口,可以改善实时流延时问题 + public byte byIntervalBPFrame;//0-BBP帧; 1-BP帧; 2-单P帧 + public byte byENumber;//E帧数量 + + +} + + public static class NET_DVR_RECORDSCHED extends Structure //时间段录像参数配置(子结构) + { + public NET_DVR_SCHEDTIME struRecordTime = new NET_DVR_SCHEDTIME() ; + public byte byRecordType; //0:定时录像,1:移动侦测,2:报警录像,3:动测|报警,4:动测&报警, 5:命令触发, 6: 智能录像 + public byte[] reservedData = new byte[3]; + + + } + + public static class NET_DVR_RECORDDAY extends Structure //全天录像参数配置(子结构) + { + public short wAllDayRecord; /* 是否全天录像 0-否 1-是*/ + public byte byRecordType; /* 录象类型 0:定时录像,1:移动侦测,2:报警录像,3:动测|报警,4:动测&报警 5:命令触发, 6: 智能录像*/ + public byte reservedData; + + + } + + public static class NET_DVR_RECORDSCHEDWEEK extends Structure + { + public NET_DVR_RECORDSCHED[] struRecordSched = new NET_DVR_RECORDSCHED[MAX_TIMESEGMENT_V30]; + + + } + + public static class NET_DVR_RECORD_V30 extends Structure { //通道录像参数配置(9000扩展) + public int dwSize; + public int dwRecord; /*是否录像 0-否 1-是*/ + public NET_DVR_RECORDDAY[] struRecAllDay = new NET_DVR_RECORDDAY[MAX_DAYS]; + public NET_DVR_RECORDSCHEDWEEK[] struRecordSched = new NET_DVR_RECORDSCHEDWEEK[MAX_DAYS]; + public int dwRecordTime; /* 录象延时长度 0-5秒, 1-20秒, 2-30秒, 3-1分钟, 4-2分钟, 5-5分钟, 6-10分钟*/ + public int dwPreRecordTime; /* 预录时间 0-不预录 1-5秒 2-10秒 3-15秒 4-20秒 5-25秒 6-30秒 7-0xffffffff(尽可能预录) */ + public int dwRecorderDuration; /* 录像保存的最长时间 */ + public byte byRedundancyRec; /*是否冗余录像,重要数据双备份:0/1*/ + public byte byAudioRec; /*录像时复合流编码时是否记录音频数据:国外有此法规*/ + public byte[] byReserve = new byte[10]; + + + } + + public static class NET_DVR_RECORD extends Structure { //通道录像参数配置 + public int dwSize; + public int dwRecord; /*是否录像 0-否 1-是*/ + public NET_DVR_RECORDDAY[] struRecAllDay = new NET_DVR_RECORDDAY[MAX_DAYS]; + public NET_DVR_RECORDSCHEDWEEK[] struRecordSched = new NET_DVR_RECORDSCHEDWEEK[MAX_DAYS]; + public int dwRecordTime; /* 录象时间长度 0-5秒, 1-20秒, 2-30秒, 3-1分钟, 4-2分钟, 5-5分钟, 6-10分钟*/ + public int dwPreRecordTime; /* 预录时间 0-不预录 1-5秒 2-10秒 3-15秒 4-20秒 5-25秒 6-30秒 7-0xffffffff(尽可能预录) */ + + +} + +public static class NET_DVR_STATFRAME extends Structure { //单帧统计参数 + public int dwRelativeTime; + public int dwAbsTime; /*统计绝对时标*/ + public byte[] byRes = new byte[92]; + + +} + +public static class NET_DVR_STATTIME extends Structure { //单帧统计参数 + public NET_DVR_TIME tmStart; //统计开始时间 + public NET_DVR_TIME tmEnd; //统计结束时间 + public byte[] byRes = new byte[92]; + + +} + + public static class UNION_PDC_STATPARAM extends Union +{ + // public byte[] byLen = new byte[140]; + public NET_DVR_STATFRAME struStatFrame; + public NET_DVR_STATTIME struStatTime; + + +} + +public static class NET_DVR_PDC_ALRAM_INFO extends Structure { //通道录像参数配置 + public int dwSize; + public byte byMode; /*0-单帧统计结果,1-最小时间段统计结果*/ + public byte byChannel; + public byte bySmart; //专业智能返回0,Smart 返回 1 + public byte byRes1; // 保留字节 + public NET_VCA_DEV_INFO struDevInfo = new NET_VCA_DEV_INFO(); //前端设备信息 + public UNION_PDC_STATPARAM uStatModeParam = new UNION_PDC_STATPARAM(); + public int dwLeaveNum; /* 离开人数 */ + public int dwEnterNum; /* 进入人数 */ + public byte byBrokenNetHttp; //断网续传标志位,0-不是重传数据,1-重传数据 + public byte byRes3; + public short wDevInfoIvmsChannelEx; //与NET_VCA_DEV_INFO里的byIvmsChannel含义相同,能表示更大的值。老客户端用byIvmsChannel能继续兼容,但是最大到255。新客户端版本请使用wDevInfoIvmsChannelEx + public int dwPassingNum; // 经过人数(进入区域后徘徊没有触发进入、离开的人数) + public byte[] byRes2 = new byte[32]; + public void read() + { + super.read(); + switch(byMode) + { + case 0: + uStatModeParam.setType(NET_DVR_STATFRAME.class); + break; + case 1: + uStatModeParam.setType(NET_DVR_STATTIME.class); + break; + default: + break; + } + uStatModeParam.read(); + } + public void write() + { + super.write(); + uStatModeParam.write(); + } + + +} + +//云台协议表结构配置 + public static class NET_DVR_PTZ_PROTOCOL extends Structure { + public int dwType; /*解码器类型值,从1开始连续递增*/ + public byte[] byDescribe = new byte[DESC_LEN]; /*解码器的描述符,和8000中的一致*/ + + +} + + public static class NET_DVR_PTZCFG extends Structure { + public int dwSize; + public NET_DVR_PTZ_PROTOCOL[] struPtz = new NET_DVR_PTZ_PROTOCOL[PTZ_PROTOCOL_NUM];/*最大200中PTZ协议*/ + public int dwPtzNum; /*有效的ptz协议数目,从0开始(即计算时加1)*/ + public byte[] byRes = new byte[8]; + + +} +/***************************云台类型(end)******************************/ + public static class NET_DVR_DECODERCFG_V30 extends Structure {//通道解码器(云台)参数配置(9000扩展) + public int dwSize; + public int dwBaudRate; //波特率(bps),0-50,1-75,2-110,3-150,4-300,5-600,6-1200,7-2400,8-4800,9-9600,10-19200, 11-38400,12-57600,13-76800,14-115.2k; + public byte byDataBit; // 数据有几位 0-5位,1-6位,2-7位,3-8位; + public byte byStopBit; // 停止位 0-1位,1-2位; + public byte byParity; // 校验 0-无校验,1-奇校验,2-偶校验; + public byte byFlowcontrol; // 0-无,1-软流控,2-硬流控 + public short wDecoderType; //解码器类型, 0-YouLi,1-LiLin-1016,2-LiLin-820,3-Pelco-p,4-DM DynaColor,5-HD600,6-JC-4116,7-Pelco-d WX,8-Pelco-d PICO + public short wDecoderAddress; /*解码器地址:0 - 255*/ + public byte[] bySetPreset = new byte[MAX_PRESET_V30]; /* 预置点是否设置,0-没有设置,1-设置*/ + public byte[] bySetCruise = new byte[MAX_CRUISE_V30]; /* 巡航是否设置: 0-没有设置,1-设置 */ + public byte[] bySetTrack = new byte[MAX_TRACK_V30]; /* 轨迹是否设置,0-没有设置,1-设置*/ + + +} + + public static class NET_DVR_DECODERCFG extends Structure {//通道解码器(云台)参数配置 + public int dwSize; + public int dwBaudRate; //波特率(bps),0-50,1-75,2-110,3-150,4-300,5-600,6-1200,7-2400,8-4800,9-9600,10-19200, 11-38400,12-57600,13-76800,14-115.2k; + public byte byDataBit; // 数据有几位 0-5位,1-6位,2-7位,3-8位; + public byte byStopBit; // 停止位 0-1位,1-2位; + public byte byParity; // 校验 0-无校验,1-奇校验,2-偶校验; + public byte byFlowcontrol; // 0-无,1-软流控,2-硬流控 + public short wDecoderType; //解码器类型, 0-YouLi,1-LiLin-1016,2-LiLin-820,3-Pelco-p,4-DM DynaColor,5-HD600,6-JC-4116,7-Pelco-d WX,8-Pelco-d PICO + public short wDecoderAddress; /*解码器地址:0 - 255*/ + public byte[] bySetPreset = new byte[MAX_PRESET]; /* 预置点是否设置,0-没有设置,1-设置*/ + public byte[] bySetCruise = new byte[MAX_CRUISE]; /* 巡航是否设置: 0-没有设置,1-设置 */ + public byte[] bySetTrack = new byte[MAX_TRACK]; /* 轨迹是否设置,0-没有设置,1-设置*/ + + +} + +public static class NET_DVR_PPPCFG_V30 extends Structure {//ppp参数配置(子结构) + public NET_DVR_IPADDR struRemoteIP; //远端IP地址 + public NET_DVR_IPADDR struLocalIP; //本地IP地址 + public byte[] sLocalIPMask = new byte[16]; //本地IP地址掩码 + public byte[] sUsername = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public byte byPPPMode; //PPP模式, 0-主动,1-被动 + public byte byRedial; //是否回拨 :0-否,1-是 + public byte byRedialMode; //回拨模式,0-由拨入者指定,1-预置回拨号码 + public byte byDataEncrypt; //数据加密,0-否,1-是 + public int dwMTU; //MTU + public byte[] sTelephoneNumber = new byte[PHONENUMBER_LEN]; //电话号码 + + +} + +public static class NET_DVR_PPPCFG extends Structure {//ppp参数配置(子结构) + public byte[] sRemoteIP = new byte[16]; //远端IP地址 + public byte[] sLocalIP = new byte[16]; //本地IP地址 + public byte[] sLocalIPMask = new byte[16]; //本地IP地址掩码 + public byte[] sUsername = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public byte byPPPMode; //PPP模式, 0-主动,1-被动 + public byte byRedial; //是否回拨 :0-否,1-是 + public byte byRedialMode; //回拨模式,0-由拨入者指定,1-预置回拨号码 + public byte byDataEncrypt; //数据加密,0-否,1-是 + public int dwMTU; //MTU + public byte[] sTelephoneNumber = new byte[PHONENUMBER_LEN]; //电话号码 + + +} + + +public static class NET_DVR_SINGLE_RS232 extends Structure {//RS232串口参数配置(9000扩展) + public int dwBaudRate; /*波特率(bps),0-50,1-75,2-110,3-150,4-300,5-600,6-1200,7-2400,8-4800,9-9600,10-19200, 11-38400,12-57600,13-76800,14-115.2k;*/ + public byte byDataBit; /* 数据有几位 0-5位,1-6位,2-7位,3-8位 */ + public byte byStopBit; /* 停止位 0-1位,1-2位 */ + public byte byParity; /* 校验 0-无校验,1-奇校验,2-偶校验 */ + public byte byFlowcontrol; /* 0-无,1-软流控,2-硬流控 */ + public int dwWorkMode; /* 工作模式,0-232串口用于PPP拨号,1-232串口用于参数控制,2-透明通道 */ + + +} + +public static class NET_DVR_RS232CFG_V30 extends Structure {//RS232串口参数配置(9000扩展) + public int dwSize; + public NET_DVR_SINGLE_RS232 struRs232;/*目前只有第一个串口设置有效,所有设备都只支持一个串口,其他七个保留*/ + public byte[] byRes = new byte[84]; + public NET_DVR_PPPCFG_V30 struPPPConfig;/*ppp参数*/ + + +} + +public static class NET_DVR_RS232CFG extends Structure {//RS232串口参数配置 + public int dwSize; + public int dwBaudRate;//波特率(bps),0-50,1-75,2-110,3-150,4-300,5-600,6-1200,7-2400,8-4800,9-9600,10-19200, 11-38400,12-57600,13-76800,14-115.2k; + public byte byDataBit;// 数据有几位 0-5位,1-6位,2-7位,3-8位; + public byte byStopBit;// 停止位 0-1位,1-2位; + public byte byParity;// 校验 0-无校验,1-奇校验,2-偶校验; + public byte byFlowcontrol;// 0-无,1-软流控,2-硬流控 + public int dwWorkMode;// 工作模式,0-窄带传输(232串口用于PPP拨号),1-控制台(232串口用于参数控制),2-透明通道 + public NET_DVR_PPPCFG struPPPConfig; + + +} + +public static class NET_DVR_ALARMINCFG_V30 extends Structure {//报警输入参数配置(9000扩展) + public int dwSize; + public byte[] sAlarmInName = new byte[NAME_LEN]; /* 名称 */ + public byte byAlarmType; //报警器类型,0:常开,1:常闭 + public byte byAlarmInHandle; /* 是否处理 0-不处理 1-处理*/ + public byte[] reservedData = new byte[2]; + public NET_DVR_HANDLEEXCEPTION_V30 struAlarmHandleType; /* 处理方式 */ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];//布防时间 + public byte[] byRelRecordChan = new byte[MAX_CHANNUM_V30]; //报警触发的录象通道,为1表示触发该通道 + public byte[] byEnablePreset = new byte[MAX_CHANNUM_V30]; /* 是否调用预置点 0-否,1-是*/ + public byte[] byPresetNo = new byte[MAX_CHANNUM_V30]; /* 调用的云台预置点序号,一个报警输入可以调用多个通道的云台预置点, 0xff表示不调用预置点。*/ + public byte[] byEnablePresetRevert = new byte[MAX_CHANNUM_V30]; /* 是否恢复到调用预置点前的位置(保留) */ + public short[] wPresetRevertDelay = new short[MAX_CHANNUM_V30]; /* 恢复预置点延时(保留) */ + public byte[] byEnableCruise = new byte[MAX_CHANNUM_V30]; /* 是否调用巡航 0-否,1-是*/ + public byte[] byCruiseNo = new byte[MAX_CHANNUM_V30]; /* 巡航 */ + public byte[] byEnablePtzTrack = new byte[MAX_CHANNUM_V30]; /* 是否调用轨迹 0-否,1-是*/ + public byte[] byPTZTrack = new byte[MAX_CHANNUM_V30]; /* 调用的云台的轨迹序号 */ + public byte[] byRes = new byte[16]; + + +} + +public static class NET_DVR_ALARMINCFG extends Structure {//报警输入参数配置 + public int dwSize; + public byte[] sAlarmInName = new byte[NAME_LEN]; /* 名称 */ + public byte byAlarmType; //报警器类型,0:常开,1:常闭 + public byte byAlarmInHandle; /* 是否处理 0-不处理 1-处理*/ + public NET_DVR_HANDLEEXCEPTION struAlarmHandleType; /* 处理方式 */ + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];//布防时间 + public byte[] byRelRecordChan = new byte[MAX_CHANNUM]; //报警触发的录象通道,为1表示触发该通道 + public byte[] byEnablePreset = new byte[MAX_CHANNUM]; /* 是否调用预置点 0-否,1-是*/ + public byte[] byPresetNo = new byte[MAX_CHANNUM]; /* 调用的云台预置点序号,一个报警输入可以调用多个通道的云台预置点, 0xff表示不调用预置点。*/ + public byte[] byEnableCruise = new byte[MAX_CHANNUM]; /* 是否调用巡航 0-否,1-是*/ + public byte[] byCruiseNo = new byte[MAX_CHANNUM]; /* 巡航 */ + public byte[] byEnablePtzTrack = new byte[MAX_CHANNUM]; /* 是否调用轨迹 0-否,1-是*/ + public byte[] byPTZTrack = new byte[MAX_CHANNUM]; /* 调用的云台的轨迹序号 */ + + +} + +public static class NET_DVR_ADDIT_POSITION extends Structure {//车载GPS信息结构(2007-12-27) + public byte[] sDevName = new byte[32]; /* 设备名称 */ + public int dwSpeed; /*速度*/ + public int dwLongitude; /* 经度*/ + public int dwLatitude; /* 纬度*/ + public byte[] direction = new byte[2]; /* direction[0]:'E'or'W'(东经/西经), direction[1]:'N'or'S'(北纬/南纬) */ + public byte[] res = new byte[2]; /* 保留位 */ + + +} + +public static class struRecordingHost extends Structure{ + public byte bySubAlarmType; + public byte[] byRes1 = new byte[3]; + public NET_DVR_TIME_EX struRecordEndTime = new NET_DVR_TIME_EX(); + public byte[] byRes = new byte[116]; + +} + +public static class struAlarmHardDisk extends Structure{ + public int dwAlarmHardDiskNum; + + +} + +public static class struAlarmChannel extends Structure{ + public int dwAlarmChanNum; + + +} + +public static class struIOAlarm extends Structure{ + public int dwAlarmInputNo; + public int dwTrigerAlarmOutNum; + public int dwTrigerRecordChanNum; + + + } + +public static class NET_DVR_TIME_EX extends Structure{ + public short wYear; + public byte byMonth; + public byte byDay; + public byte byHour; + public byte byMinute; + public byte bySecond; + public byte byRes; + + +} + +public static class uStruAlarm extends Union +{ + public byte[] byUnionLen = new byte[128]; + public struIOAlarm struioAlarm = new struIOAlarm(); + public struAlarmHardDisk strualarmHardDisk = new struAlarmHardDisk(); + public struAlarmChannel strualarmChannel = new struAlarmChannel(); + public struRecordingHost strurecordingHost = new struRecordingHost(); + + +} + +public static class NET_DVR_ALRAM_FIXED_HEADER extends Structure{ + public int dwAlarmType; + public NET_DVR_TIME_EX struAlarmTime = new NET_DVR_TIME_EX(); + public uStruAlarm ustruAlarm = new uStruAlarm(); + + +} + +public static class NET_DVR_ALARMINFO_V40 extends Structure { + public NET_DVR_ALRAM_FIXED_HEADER struAlarmFixedHeader = new NET_DVR_ALRAM_FIXED_HEADER(); + public Pointer pAlarmData; + + +} +public static class NET_DVR_ALARMINFO_V30 extends Structure {//上传报警信息(9000扩展) + public int dwAlarmType;/*0-信号量报警,1-硬盘满,2-信号丢失,3-移动侦测,4-硬盘未格式化,5-读写硬盘出错,6-遮挡报警,7-制式不匹配, 8-非法访问, 0xa-GPS定位信息(车载定制)*/ + public int dwAlarmInputNumber;/*报警输入端口*/ + public byte[] byAlarmOutputNumber = new byte[MAX_ALARMOUT_V30];/*触发的输出端口,为1表示对应输出*/ + public byte[] byAlarmRelateChannel= new byte[MAX_CHANNUM_V30];/*触发的录像通道,为1表示对应录像, dwAlarmRelateChannel[0]对应第1个通道*/ + public byte[] byChannel= new byte[MAX_CHANNUM_V30];/*dwAlarmType为2或3,6时,表示哪个通道,dwChannel[0]对应第1个通道*/ + public byte[] byDiskNumber= new byte[MAX_DISKNUM_V30];/*dwAlarmType为1,4,5时,表示哪个硬盘, dwDiskNumber[0]对应第1个硬盘*/ + + +} + +public static class NET_DVR_ALARMINFO extends Structure { + public int dwAlarmType;/*0-信号量报警,1-硬盘满,2-信号丢失,3-移动侦测,4-硬盘未格式化,5-读写硬盘出错,6-遮挡报警,7-制式不匹配, 8-非法访问, 9-串口状态, 0xa-GPS定位信息(车载定制)*/ + public int dwAlarmInputNumber;/*报警输入端口, 当报警类型为9时该变量表示串口状态0表示正常, -1表示错误*/ + public int[] dwAlarmOutputNumber = new int[MAX_ALARMOUT];/*触发的输出端口,为1表示对应哪一个输出*/ + public int[] dwAlarmRelateChannel = new int[MAX_CHANNUM];/*触发的录像通道,dwAlarmRelateChannel[0]为1表示第1个通道录像*/ + public int[] dwChannel = new int[MAX_CHANNUM];/*dwAlarmType为2或3,6时,表示哪个通道,dwChannel[0]位对应第1个通道*/ + public int[] dwDiskNumber = new int[MAX_DISKNUM];/*dwAlarmType为1,4,5时,表示哪个硬盘, dwDiskNumber[0]位对应第1个硬盘*/ + + +} + +public static class NET_DVR_ALARMINFO_EX extends Structure {//上传报警信息(杭州竞天定制 2006-07-28) + public int dwAlarmType;/*0-信号量报警,1-硬盘满,2-信号丢失,3-移动侦测,4-硬盘未格式化,5-读写硬盘出错,6-遮挡报警,7-制式不匹配, 8-非法访问*/ + public int dwAlarmInputNumber;/*报警输入端口*/ + public int[] dwAlarmOutputNumber = new int[MAX_ALARMOUT];/*报警输入端口对应的输出端口,哪一位为1表示对应哪一个输出*/ + public int[] dwAlarmRelateChannel = new int[MAX_CHANNUM];/*报警输入端口对应的录像通道,哪一位为1表示对应哪一路录像,dwAlarmRelateChannel[0]对应第1个通道*/ + public int[] dwChannel = new int[MAX_CHANNUM];/*dwAlarmType为2或3,6时,表示哪个通道,dwChannel[0]位对应第0个通道*/ + public int[] dwDiskNumber = new int[MAX_DISKNUM];/*dwAlarmType为1,4,5时,表示哪个硬盘*/ + public byte[] sSerialNumber = new byte[SERIALNO_LEN]; //序列号 + public byte[] sRemoteAlarmIP = new byte[16]; //远程报警IP地址; + + +} + +////////////////////////////////////////////////////////////////////////////////////// +//IPC接入参数配置 +public static class NET_DVR_IPDEVINFO extends Structure {/* IP设备结构 */ + public int dwEnable; /* 该IP设备是否启用 */ + public byte[] sUserName = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public NET_DVR_IPADDR struIP = new NET_DVR_IPADDR(); /* IP地址 */ + public short wDVRPort; /* 端口号 */ + public byte[] byres = new byte[34]; /* 保留 */ + + +} + +public static class NET_DVR_IPCHANINFO extends Structure {/* IP通道匹配参数 */ + public byte byEnable; /* 该通道是否启用 */ + public byte byIPID; /* IP设备ID 取值1- MAX_IP_DEVICE */ + public byte byChannel; /* 通道号 */ + public byte[] byres = new byte[33]; /* 保留 */ + + +} + +public static class NET_DVR_IPPARACFG extends Structure {/* IP接入配置结构 */ + public int dwSize; /* 结构大小 */ + public NET_DVR_IPDEVINFO[] struIPDevInfo = new NET_DVR_IPDEVINFO[MAX_IP_DEVICE]; /* IP设备 */ + public byte[] byAnalogChanEnable = new byte[MAX_ANALOG_CHANNUM]; /* 模拟通道是否启用,从低到高表示1-32通道,0表示无效 1有效 */ + public NET_DVR_IPCHANINFO[] struIPChanInfo = new NET_DVR_IPCHANINFO[MAX_IP_CHANNEL]; /* IP通道 */ + + +} + +public class NET_DVR_IPDEVINFO_V31 extends Structure { + public byte byEnable;/* 该通道是否启用 */ + public byte byProType;//协议类型(默认为私有协议),0- 私有协议,1- 松下协议,2- 索尼,更多协议通过NET_DVR_GetIPCProtoList获取。 + public byte byEnableQuickAdd;//0-不支持快速添加;1-使用快速添加 + public byte byRes1;//保留,置为0 + public byte[] sUserName =new byte[HCNetSDK.NAME_LEN];//用户名 + public byte[] sPassword = new byte[HCNetSDK.PASSWD_LEN];//密码 + public byte[] byDomain = new byte[HCNetSDK.MAX_DOMAIN_NAME];//设备域名 + public NET_DVR_IPADDR struIP = new NET_DVR_IPADDR();//IP地址 + public short wDVRPort;//端口号 + public byte[] szDeviceID= new byte[32]; + public byte[] byRes2 = new byte[2];//保留,置为0 + + +} + +public class NET_DVR_STREAM_MODE extends Structure { + + public byte byGetStreamType;//取流方式:0- 直接从设备取流;1- 从流媒体取流;2- 通过IPServer获得IP地址后取流; + //3- 通过IPServer找到设备,再通过流媒体取设备的流; 4- 通过流媒体由URL去取流;5- 通过hiDDNS域名连接设备然后从设备取流 + public byte[] byRes= new byte[3];//保留,置为0 + public NET_DVR_GET_STREAM_UNION uGetStream =new NET_DVR_GET_STREAM_UNION();//不同取流方式联合体 + + public void read(){ + super.read(); + switch(byGetStreamType) + { + case 0: + uGetStream.setType(NET_DVR_IPCHANINFO.class); + break; + case 6: + uGetStream.setType(NET_DVR_IPCHANINFO_V40.class); + break; + default: + break; + } + } + + +} + +public class NET_DVR_IPSERVER_STREAM extends Structure{ + public byte byEnable; + public byte[] byRes=new byte[3]; + public NET_DVR_IPADDR struIPServer=new NET_DVR_IPADDR(); + public short wPort; + public short wDvrNameLen; + public byte[] byDVRName=new byte[HCNetSDK.NAME_LEN]; + public short wDVRSerialLen; + public short[] byRes1=new short[2]; + public byte[] byDVRSerialNumber=new byte[HCNetSDK.SERIALNO_LEN]; + public byte[] byUserName=new byte[HCNetSDK.NAME_LEN]; + public byte[] byPassWord=new byte[HCNetSDK.PASSWD_LEN]; + public byte byChannel; + public byte[] byRes2=new byte[11]; + + +} + +public class NET_DVR_STREAM_MEDIA_SERVER_CFG extends Structure{ + + public byte byValid;//是否启用流媒体服务器取流:0-不启用,非0-启用 + public byte[] byRes1=new byte[3];//保留,置为0 + public NET_DVR_IPADDR struDevIP=new NET_DVR_IPADDR();//流媒体服务器的IP地址 + public short wDevPort;//流媒体服务器端口 + public byte byTransmitType;//传输协议类型:0-TCP,1-UDP + public byte[] byRes2=new byte[69]; + + +} + +public class NET_DVR_DEV_CHAN_INFO extends Structure{ + public NET_DVR_IPADDR struIP =new NET_DVR_IPADDR();//设备IP地址 + public short wDVRPort;//设备端口号 + public byte byChannel;//通道号,目前设备的模拟通道号是从1开始的,对于9000等设备的IPC接入,数字通道号从33开始 + public byte byTransProtocol;//传输协议类型:0-TCP,1-UDP,2-多播方式,3-RTP + public byte byTransMode;//传输码流模式:0-主码流,1-子码流 + public byte byFactoryType;//前端设备厂家类型, 通过接口NET_DVR_GetIPCProtoList获取 + public byte byDeviceType;//设备类型(视频综合平台使用):1- IPC,2- ENCODER + public byte byDispChan;// 显示通道号(智能配置使用),根据能力集决定使用解码通道还是显示通道 + public byte bySubDispChan;//显示通道子通道号(智能配置时使用) + public byte byResolution;//分辨率:1- CIF,2- 4CIF,3- 720P,4- 1080P,5- 500W,用于多屏控制器,多屏控制器会根据该参数分配解码资源 + public byte[] byRes=new byte[2];//保留,置为0 + public byte[] byDomain =new byte[HCNetSDK.MAX_DOMAIN_NAME];//设备域名 + public byte[] sUserName=new byte[HCNetSDK.NAME_LEN];//设备登陆帐号 + public byte[] sPassword=new byte[HCNetSDK.PASSWD_LEN];//设备密码 + + +} + +public class NET_DVR_PU_STREAM_CFG extends Structure{ + public int dwSize;//结构体大小 + public NET_DVR_STREAM_MEDIA_SERVER_CFG struStreamMediaSvrCfg=new NET_DVR_STREAM_MEDIA_SERVER_CFG(); + public NET_DVR_DEV_CHAN_INFO struDevChanInfo=new NET_DVR_DEV_CHAN_INFO(); + + +} + +public class NET_DVR_DDNS_STREAM_CFG extends Structure{ + public byte byEnable; + public byte[] byRes1=new byte[3]; + public NET_DVR_IPADDR struStreamServer=new NET_DVR_IPADDR(); + public short wStreamServerPort; + public byte byStreamServerTransmitType; + public byte byRes2; + public NET_DVR_IPADDR struIPServer=new NET_DVR_IPADDR(); + public short wIPServerPort; + public byte[] byRes3=new byte[2]; + public byte[] sDVRName=new byte[HCNetSDK.NAME_LEN]; + public short wDVRNameLen; + public short wDVRSerialLen; + public byte[] sDVRSerialNumber=new byte[HCNetSDK.SERIALNO_LEN]; + public byte[] sUserName=new byte[HCNetSDK.NAME_LEN]; + public byte[] sPassWord=new byte[HCNetSDK.PASSWD_LEN]; + public short wDVRPort; + public byte[] byRes4=new byte[2]; + public byte byChannel; + public byte byTransProtocol; + public byte byTransMode; + public byte byFactoryType; + + +} + +public class NET_DVR_PU_STREAM_URL extends Structure{ + public byte byEnable;//是否启用:0- 禁用,1- 启用 + public byte[] strURL=new byte[240];//取流URL路径 + public byte byTransPortocol;//传输协议类型:0-TCP,1-UDP + public short wIPID;//设备ID号,wIPID = iDevInfoIndex + iGroupNO*64 +1 + public byte byChannel;//设备通道号 + public byte[] byRes=new byte[7];//保留,置为0 + + +} + +public class NET_DVR_HKDDNS_STREAM extends Structure{ + public byte byEnable;//是否启用 + public byte[] byRes=new byte[3];//保留 + public byte[] byDDNSDomain=new byte[64];//hiDDNS服务器地址 + public short wPort;//hiDDNS端口,默认:80 + public short wAliasLen;//别名长度 + public byte[] byAlias=new byte[HCNetSDK.NAME_LEN];//别名 + public short wDVRSerialLen;//序列号长度 + public byte[] byRes1=new byte[2];//保留 + public byte[] byDVRSerialNumber=new byte[HCNetSDK.SERIALNO_LEN];//设备序列号 + public byte[] byUserName=new byte[HCNetSDK.NAME_LEN];//设备登录用户名 + public byte[] byPassWord=new byte[HCNetSDK.PASSWD_LEN];//设备登录密码 + public byte byChannel;//设备通道号 + public byte[] byRes2=new byte[11];//保留 + + +} + +public class NET_DVR_IPCHANINFO_V40 extends Structure { + + public byte byEnable;//IP通道在线状态,是一个只读的属性; + //0表示HDVR或者NVR设备的数字通道连接对应的IP设备失败,该通道不在线;1表示连接成功,该通道在线 + public byte byRes1;//保留,置为0 + public short wIPID;//IP设备ID + public int dwChannel;//IP设备的通道号,例如设备A(HDVR或者NVR设备)的IP通道01,对应的是设备B(DVS)里的通道04,则byChannel=4,如果前端接的是IPC则byChannel=1。 + public byte byTransProtocol;//传输协议类型:0- TCP,1- UDP,2- 多播,0xff- auto(自动) + public byte byTransMode;//传输码流模式:0- 主码流,1- 子码流 + public byte byFactoryType;//前端设备厂家类型 + public byte[] byRes = new byte[241];//保留,置为0 + + +} + + +public static class NET_DVR_GET_STREAM_UNION extends Union +{ + public NET_DVR_IPCHANINFO struChanInfo = new NET_DVR_IPCHANINFO(); /*IP通道信息*/ + public NET_DVR_IPCHANINFO_V40 struIPChan = new NET_DVR_IPCHANINFO_V40(); //直接从设备取流(扩展) + public byte[] byUnionLen = new byte[492]; //直接从设备取流(扩展) + + +} + +public static class NET_DVR_IPPARACFG_V40 extends Structure {/* IP接入配置结构V40 */ + public int dwSize; /* 结构大小 */ + public int dwGroupNum;//设备支持的总组数(只读)。 + public int dwAChanNum;//最大模拟通道个数(只读) + public int dwDChanNum;//数字通道个数(只读) + public int dwStartDChan;//起始数字通道(只读) + public byte[] byAnalogChanEnable = new byte[MAX_CHANNUM_V30]; //模拟通道资源是否启用,从低到高表示1-64通道:0-禁用,1-启用。 + public NET_DVR_IPDEVINFO_V31[] struIPDevInfo = new NET_DVR_IPDEVINFO_V31[MAX_IP_DEVICE_V40];//IP设备信息,下标0对应设备IP ID为1 + public NET_DVR_STREAM_MODE[] struStreamMode =new NET_DVR_STREAM_MODE[MAX_CHANNUM_V30];//取流模式 + public byte[] byRes2=new byte[20];//保留,置为0 + + +} + +public static class NET_DVR_IPALARMOUTINFO extends Structure {/* 报警输出参数 */ + public byte byIPID; /* IP设备ID取值1- MAX_IP_DEVICE */ + public byte byAlarmOut; /* 报警输出号 */ + public byte[] byRes = new byte[18]; /* 保留 */ + + +} + +public static class NET_DVR_IPALARMOUTCFG extends Structure {/* IP报警输出配置结构 */ + public int dwSize; /* 结构大小 */ + public NET_DVR_IPALARMOUTINFO[] struIPAlarmOutInfo = new NET_DVR_IPALARMOUTINFO[MAX_IP_ALARMOUT];/* IP报警输出 */ + + +} + +public static class NET_DVR_IPALARMININFO extends Structure {/* 报警输入参数 */ + public byte byIPID; /* IP设备ID取值1- MAX_IP_DEVICE */ + public byte byAlarmIn; /* 报警输入号 */ + public byte[] byRes = new byte[18]; /* 保留 */ + + +} + +public static class NET_DVR_IPALARMINCFG extends Structure {/* IP报警输入配置结构 */ + public int dwSize; /* 结构大小 */ + public NET_DVR_IPALARMININFO[] struIPAlarmInInfo = new NET_DVR_IPALARMININFO[MAX_IP_ALARMIN];/* IP报警输入 */ + + +} + +public static class NET_DVR_IPALARMINFO extends Structure {//ipc alarm info + public NET_DVR_IPDEVINFO[] struIPDevInfo = new NET_DVR_IPDEVINFO[MAX_IP_DEVICE]; /* IP设备 */ + public byte[] byAnalogChanEnable = new byte[MAX_ANALOG_CHANNUM]; /* 模拟通道是否启用,0-未启用 1-启用 */ + public NET_DVR_IPCHANINFO[] struIPChanInfo = new NET_DVR_IPCHANINFO[MAX_IP_CHANNEL]; /* IP通道 */ + public NET_DVR_IPALARMININFO[] struIPAlarmInInfo = new NET_DVR_IPALARMININFO[MAX_IP_ALARMIN]; /* IP报警输入 */ + public NET_DVR_IPALARMOUTINFO[] struIPAlarmOutInfo = new NET_DVR_IPALARMOUTINFO[MAX_IP_ALARMOUT]; /* IP报警输出 */ + + +} + +public static class NET_DVR_SINGLE_HD extends Structure {//本地硬盘信息配置 + public int dwHDNo; /*硬盘号, 取值0~MAX_DISKNUM_V30-1*/ + public int dwCapacity; /*硬盘容量(不可设置)*/ + public int dwFreeSpace; /*硬盘剩余空间(不可设置)*/ + public int dwHdStatus; /*硬盘状态(不可设置) 0-正常, 1-未格式化, 2-错误, 3-SMART状态, 4-不匹配, 5-休眠*/ + public byte byHDAttr; /*0-默认, 1-冗余; 2-只读*/ + public byte[] byRes1 = new byte[3]; + public int dwHdGroup; /*属于哪个盘组 1-MAX_HD_GROUP*/ + public byte[] byRes2 = new byte[120]; +} + +public static class NET_DVR_HDCFG extends Structure { + public int dwSize; + public int dwHDCount; /*硬盘数(不可设置)*/ + public NET_DVR_SINGLE_HD[] struHDInfo = new NET_DVR_SINGLE_HD[MAX_DISKNUM_V30];//硬盘相关操作都需要重启才能生效; +} + +public static class NET_DVR_SINGLE_HDGROUP extends Structure {//本地盘组信息配置 + public int dwHDGroupNo; /*盘组号(不可设置) 1-MAX_HD_GROUP*/ + public byte[] byHDGroupChans = new byte[64]; /*盘组对应的录像通道, 0-表示该通道不录象到该盘组,1-表示录象到该盘组*/ + public byte[] byRes = new byte[8]; +} + +public static class NET_DVR_HDGROUP_CFG extends Structure { + public int dwSize; + public int dwHDGroupCount; /*盘组总数(不可设置)*/ + public NET_DVR_SINGLE_HDGROUP[] struHDGroupAttr = new NET_DVR_SINGLE_HDGROUP[MAX_HD_GROUP];//硬盘相关操作都需要重启才能生效; +} + +public static class NET_DVR_SCALECFG extends Structure {//配置缩放参数的结构 + public int dwSize; + public int dwMajorScale; /* 主显示 0-不缩放,1-缩放*/ + public int dwMinorScale; /* 辅显示 0-不缩放,1-缩放*/ + public int[] dwRes = new int[2]; +} + +public static class NET_DVR_ALARMOUTCFG_V30 extends Structure {//DVR报警输出(9000扩展) + public int dwSize; + public byte[] sAlarmOutName = new byte[NAME_LEN]; /* 名称 */ + public int dwAlarmOutDelay; /* 输出保持时间(-1为无限,手动关闭) */ + //0-5秒,1-10秒,2-30秒,3-1分钟,4-2分钟,5-5分钟,6-10分钟,7-手动 + public NET_DVR_SCHEDTIMEWEEK[] struAlarmOutTime= new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];/* 报警输出激活时间段 */ + public byte[] byRes = new byte[16]; +} + +public static class NET_DVR_ALARMOUTCFG extends Structure {//DVR报警输出 + public int dwSize; + public byte[] sAlarmOutName = new byte[NAME_LEN]; /* 名称 */ + public int dwAlarmOutDelay; /* 输出保持时间(-1为无限,手动关闭) */ + //0-5秒,1-10秒,2-30秒,3-1分钟,4-2分钟,5-5分钟,6-10分钟,7-手动 + public NET_DVR_SCHEDTIMEWEEK[] struAlarmOutTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];/* 报警输出激活时间段 */ +} + +public static class NET_DVR_PREVIEWCFG_V30 extends Structure {//DVR本地预览参数(9000扩展) + public int dwSize; + public byte byPreviewNumber;//预览数目,0-1画面,1-4画面,2-9画面,3-16画面, 4-6画面, 5-8画面, 0xff:最大画面 + public byte byEnableAudio;//是否声音预览,0-不预览,1-预览 + public short wSwitchTime;//切换时间,0-不切换,1-5s,2-10s,3-20s,4-30s,5-60s,6-120s,7-300s + public byte[][] bySwitchSeq = new byte[MAX_PREVIEW_MODE][MAX_WINDOW_V30];//切换顺序,如果lSwitchSeq[i]为 0xff表示不用 + public byte[] byRes = new byte[24]; +} + +public static class NET_DVR_PREVIEWCFG extends Structure {//DVR本地预览参数 + public int dwSize; + public byte byPreviewNumber;//预览数目,0-1画面,1-4画面,2-9画面,3-16画面,0xff:最大画面 + public byte byEnableAudio;//是否声音预览,0-不预览,1-预览 + public short wSwitchTime;//切换时间,0-不切换,1-5s,2-10s,3-20s,4-30s,5-60s,6-120s,7-300s + public byte[] bySwitchSeq = new byte[MAX_WINDOW];//切换顺序,如果lSwitchSeq[i]为 0xff表示不用 +} + +public static class NET_DVR_VGAPARA extends Structure {//DVR视频输出 + public short wResolution; /* 分辨率 */ + public short wFreq; /* 刷新频率 */ + public int dwBrightness; /* 亮度 */ +} + +/* +* MATRIX输出参数结构 +*/ +public static class NET_DVR_MATRIXPARA_V30 extends Structure { + public short[] wOrder = new short[MAX_ANALOG_CHANNUM]; /* 预览顺序, 0xff表示相应的窗口不预览 */ + public short wSwitchTime; /* 预览切换时间 */ + public byte[] res = new byte[14]; +} + +public static class NET_DVR_MATRIXPARA extends Structure { + public short wDisplayLogo; /* 显示视频通道号(保留) */ + public short wDisplayOsd; /* 显示时间(保留) */ +} + +public static class NET_DVR_VOOUT extends Structure { + public byte byVideoFormat; /* 输出制式,0-PAL,1-NTSC */ + public byte byMenuAlphaValue; /* 菜单与背景图象对比度 */ + public short wScreenSaveTime; /* 屏幕保护时间 0-从不,1-1分钟,2-2分钟,3-5分钟,4-10分钟,5-20分钟,6-30分钟 */ + public short wVOffset; /* 视频输出偏移 */ + public short wBrightness; /* 视频输出亮度 */ + public byte byStartMode; /* 启动后视频输出模式(0:菜单,1:预览)*/ + public byte byEnableScaler; /* 是否启动缩放 (0-不启动, 1-启动)*/ +} + +public static class NET_DVR_VIDEOOUT_V30 extends Structure {//DVR视频输出(9000扩展) + public int dwSize; + public NET_DVR_VOOUT[] struVOOut = new NET_DVR_VOOUT[MAX_VIDEOOUT_V30]; + public NET_DVR_VGAPARA[] struVGAPara = new NET_DVR_VGAPARA[MAX_VGA_V30]; /* VGA参数 */ + public NET_DVR_MATRIXPARA_V30[] struMatrixPara = new NET_DVR_MATRIXPARA_V30[MAX_MATRIXOUT]; /* MATRIX参数 */ + public byte[] byRes = new byte[16]; +} + +public static class NET_DVR_VIDEOOUT extends Structure {//DVR视频输出 + public int dwSize; + public NET_DVR_VOOUT[] struVOOut = new NET_DVR_VOOUT[MAX_VIDEOOUT]; + public NET_DVR_VGAPARA[] struVGAPara = new NET_DVR_VGAPARA[MAX_VGA]; /* VGA参数 */ + public NET_DVR_MATRIXPARA struMatrixPara; /* MATRIX参数 */ +} + +public static class NET_DVR_USER_INFO_V30 extends Structure {//单用户参数(子结构)(9000扩展) + public byte[] sUserName = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public byte[] byLocalRight = new byte[MAX_RIGHT]; /* 本地权限 */ + /*数组0: 本地控制云台*/ + /*数组1: 本地手动录象*/ + /*数组2: 本地回放*/ + /*数组3: 本地设置参数*/ + /*数组4: 本地查看状态、日志*/ + /*数组5: 本地高级操作(升级,格式化,重启,关机)*/ + /*数组6: 本地查看参数 */ + /*数组7: 本地管理模拟和IP camera */ + /*数组8: 本地备份 */ + /*数组9: 本地关机/重启 */ + public byte[] byRemoteRight = new byte[MAX_RIGHT];/* 远程权限 */ + /*数组0: 远程控制云台*/ + /*数组1: 远程手动录象*/ + /*数组2: 远程回放 */ + /*数组3: 远程设置参数*/ + /*数组4: 远程查看状态、日志*/ + /*数组5: 远程高级操作(升级,格式化,重启,关机)*/ + /*数组6: 远程发起语音对讲*/ + /*数组7: 远程预览*/ + /*数组8: 远程请求报警上传、报警输出*/ + /*数组9: 远程控制,本地输出*/ + /*数组10: 远程控制串口*/ + /*数组11: 远程查看参数 */ + /*数组12: 远程管理模拟和IP camera */ + /*数组13: 远程关机/重启 */ + public byte[] byNetPreviewRight = new byte[MAX_CHANNUM_V30]; /* 远程可以预览的通道 0-有权限,1-无权限*/ + public byte[] byLocalPlaybackRight = new byte[MAX_CHANNUM_V30]; /* 本地可以回放的通道 0-有权限,1-无权限*/ + public byte[] byNetPlaybackRight = new byte[MAX_CHANNUM_V30]; /* 远程可以回放的通道 0-有权限,1-无权限*/ + public byte[] byLocalRecordRight = new byte[MAX_CHANNUM_V30]; /* 本地可以录像的通道 0-有权限,1-无权限*/ + public byte[] byNetRecordRight = new byte[MAX_CHANNUM_V30]; /* 远程可以录像的通道 0-有权限,1-无权限*/ + public byte[] byLocalPTZRight = new byte[MAX_CHANNUM_V30]; /* 本地可以PTZ的通道 0-有权限,1-无权限*/ + public byte[] byNetPTZRight = new byte[MAX_CHANNUM_V30]; /* 远程可以PTZ的通道 0-有权限,1-无权限*/ + public byte[] byLocalBackupRight = new byte[MAX_CHANNUM_V30]; /* 本地备份权限通道 0-有权限,1-无权限*/ + public NET_DVR_IPADDR struUserIP; /* 用户IP地址(为0时表示允许任何地址) */ + public byte[] byMACAddr = new byte[MACADDR_LEN]; /* 物理地址 */ + public byte byPriority; /* 优先级,0xff-无,0--低,1--中,2--高 */ + /* + 无……表示不支持优先级的设置 + 低……默认权限:包括本地和远程回放,本地和远程查看日志和状态,本地和远程关机/重启 + 中……包括本地和远程控制云台,本地和远程手动录像,本地和远程回放,语音对讲和远程预览 + 本地备份,本地/远程关机/重启 + 高……管理员 + */ + public byte[] byRes = new byte[17]; +} + +public static class NET_DVR_USER_INFO_EX extends Structure {//单用户参数(SDK_V15扩展)(子结构) + public byte[] sUserName = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public int[] dwLocalRight = new int[MAX_RIGHT]; /* 权限 */ + /*数组0: 本地控制云台*/ + /*数组1: 本地手动录象*/ + /*数组2: 本地回放*/ + /*数组3: 本地设置参数*/ + /*数组4: 本地查看状态、日志*/ + /*数组5: 本地高级操作(升级,格式化,重启,关机)*/ + public int dwLocalPlaybackRight; /* 本地可以回放的通道 bit0 -- channel 1*/ + public int[] dwRemoteRight = new int[MAX_RIGHT]; /* 权限 */ + /*数组0: 远程控制云台*/ + /*数组1: 远程手动录象*/ + /*数组2: 远程回放 */ + /*数组3: 远程设置参数*/ + /*数组4: 远程查看状态、日志*/ + /*数组5: 远程高级操作(升级,格式化,重启,关机)*/ + /*数组6: 远程发起语音对讲*/ + /*数组7: 远程预览*/ + /*数组8: 远程请求报警上传、报警输出*/ + /*数组9: 远程控制,本地输出*/ + /*数组10: 远程控制串口*/ + public int dwNetPreviewRight; /* 远程可以预览的通道 bit0 -- channel 1*/ + public int dwNetPlaybackRight; /* 远程可以回放的通道 bit0 -- channel 1*/ + public byte[] sUserIP = new byte[16]; /* 用户IP地址(为0时表示允许任何地址) */ + public byte[] byMACAddr = new byte[MACADDR_LEN]; /* 物理地址 */ +} + +public static class NET_DVR_USER_INFO extends Structure {//单用户参数(子结构) + public byte[] sUserName = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public int[] dwLocalRight = new int[MAX_RIGHT]; /* 权限 */ + /*数组0: 本地控制云台*/ + /*数组1: 本地手动录象*/ + /*数组2: 本地回放*/ + /*数组3: 本地设置参数*/ + /*数组4: 本地查看状态、日志*/ + /*数组5: 本地高级操作(升级,格式化,重启,关机)*/ + public int[] dwRemoteRight = new int[MAX_RIGHT]; /* 权限 */ + /*数组0: 远程控制云台*/ + /*数组1: 远程手动录象*/ + /*数组2: 远程回放 */ + /*数组3: 远程设置参数*/ + /*数组4: 远程查看状态、日志*/ + /*数组5: 远程高级操作(升级,格式化,重启,关机)*/ + /*数组6: 远程发起语音对讲*/ + /*数组7: 远程预览*/ + /*数组8: 远程请求报警上传、报警输出*/ + /*数组9: 远程控制,本地输出*/ + /*数组10: 远程控制串口*/ + public byte[] sUserIP = new byte[16]; /* 用户IP地址(为0时表示允许任何地址) */ + public byte[] byMACAddr = new byte[MACADDR_LEN]; /* 物理地址 */ +} + +public static class NET_DVR_USER_V30 extends Structure {//DVR用户参数(9000扩展) + public int dwSize; + public NET_DVR_USER_INFO_V30[] struUser = new NET_DVR_USER_INFO_V30[MAX_USERNUM_V30]; +} + +public static class NET_DVR_USER_EX extends Structure {//DVR用户参数(SDK_V15扩展) + public int dwSize; + public NET_DVR_USER_INFO_EX[] struUser = new NET_DVR_USER_INFO_EX[MAX_USERNUM]; +} + +public static class NET_DVR_USER extends Structure {//DVR用户参数 + public int dwSize; + public NET_DVR_USER_INFO[] struUser = new NET_DVR_USER_INFO[MAX_USERNUM]; +} + +public static class NET_DVR_EXCEPTION_V30 extends Structure {//DVR异常参数(9000扩展) + public int dwSize; + public NET_DVR_HANDLEEXCEPTION_V30[] struExceptionHandleType = new NET_DVR_HANDLEEXCEPTION_V30[MAX_EXCEPTIONNUM_V30]; + /*数组0-盘满,1- 硬盘出错,2-网线断,3-局域网内IP 地址冲突,4-非法访问, 5-输入/输出视频制式不匹配, 6-行车超速(车载专用), 7-视频信号异常(9000)*/ +} + +public static class NET_DVR_EXCEPTION extends Structure {//DVR异常参数 + public int dwSize; + public NET_DVR_HANDLEEXCEPTION[] struExceptionHandleType = new NET_DVR_HANDLEEXCEPTION[MAX_EXCEPTIONNUM]; + /*数组0-盘满,1- 硬盘出错,2-网线断,3-局域网内IP 地址冲突,4-非法访问, 5-输入/输出视频制式不匹配, 6-行车超速(车载专用)*/ +} + +public static class NET_DVR_CHANNELSTATE_V30 extends Structure {//通道状态(9000扩展) + public byte byRecordStatic; //通道是否在录像,0-不录像,1-录像 + public byte bySignalStatic; //连接的信号状态,0-正常,1-信号丢失 + public byte byHardwareStatic;//通道硬件状态,0-正常,1-异常,例如DSP死掉 + public byte byRes1; //保留 + public int dwBitRate;//实际码率 + public int dwLinkNum;//客户端连接的个数 + public NET_DVR_IPADDR[] struClientIP = new NET_DVR_IPADDR[MAX_LINK];//客户端的IP地址 + public int dwIPLinkNum;//如果该通道为IP接入,那么表示IP接入当前的连接数 + public byte byExceedMaxLink; // 是否超出了单路6路连接数 0 - 未超出, 1-超出 + public byte[] byRes = new byte[3]; // 保留字节 + public int dwAllBitRate; //所有实际码率之和 + public int dwChannelNo; //当前的通道号,0xffffffff表示无效 +} + +public static class NET_DVR_CHANNELSTATE extends Structure {//通道状态 + public byte byRecordStatic; //通道是否在录像,0-不录像,1-录像 + public byte bySignalStatic; //连接的信号状态,0-正常,1-信号丢失 + public byte byHardwareStatic;//通道硬件状态,0-正常,1-异常,例如DSP死掉 + public byte reservedData; //保留 + public int dwBitRate;//实际码率 + public int dwLinkNum;//客户端连接的个数 + public int[] dwClientIP = new int[MAX_LINK];//客户端的IP地址 +} + +public static class NET_DVR_DISKSTATE extends Structure {//硬盘状态 + public int dwVolume;//硬盘的容量 + public int dwFreeSpace;//硬盘的剩余空间 + public int dwHardDiskStatic; //硬盘的状态,按位:1-休眠,2-不正常,3-休眠硬盘出错 +} + +public static class NET_DVR_WORKSTATE_V30 extends Structure {//DVR工作状态(9000扩展) + public int dwDeviceStatic; //设备的状态,0-正常,1-CPU占用率太高,超过85%,2-硬件错误,例如串口死掉 + public NET_DVR_DISKSTATE[] struHardDiskStatic = new NET_DVR_DISKSTATE[MAX_DISKNUM_V30]; + public NET_DVR_CHANNELSTATE_V30[] struChanStatic = new NET_DVR_CHANNELSTATE_V30[MAX_CHANNUM_V30];//通道的状态 + public byte[] byAlarmInStatic = new byte[MAX_ALARMIN_V30]; //报警端口的状态,0-没有报警,1-有报警 + public byte[] byAlarmOutStatic = new byte[MAX_ALARMOUT_V30]; //报警输出端口的状态,0-没有输出,1-有报警输出 + public int dwLocalDisplay;//本地显示状态,0-正常,1-不正常 + public byte [] byAudioChanStatus = new byte[MAX_AUDIO_V30];//表示语音通道的状态 0-未使用,1-使用中, 0xff无效 + public byte[] byRes = new byte[10]; +} + +public static class NET_DVR_WORKSTATE extends Structure {//DVR工作状态 + public int dwDeviceStatic; //设备的状态,0-正常,1-CPU占用率太高,超过85%,2-硬件错误,例如串口死掉 + public NET_DVR_DISKSTATE[] struHardDiskStatic = new NET_DVR_DISKSTATE[MAX_DISKNUM]; + public NET_DVR_CHANNELSTATE[] struChanStatic = new NET_DVR_CHANNELSTATE[MAX_CHANNUM];//通道的状态 + public byte[] byAlarmInStatic = new byte[MAX_ALARMIN]; //报警端口的状态,0-没有报警,1-有报警 + public byte[] byAlarmOutStatic = new byte[MAX_ALARMOUT]; //报警输出端口的状态,0-没有输出,1-有报警输出 + public int dwLocalDisplay;//本地显示状态,0-正常,1-不正常 +} + +public static class NET_DVR_LOG_V30 extends Structure {//日志信息(9000扩展) + public NET_DVR_TIME strLogTime; + public int dwMajorType; //主类型 1-报警; 2-异常; 3-操作; 0xff-全部 + public int dwMinorType;//次类型 0-全部; + public byte[] sPanelUser = new byte[MAX_NAMELEN]; //操作面板的用户名 + public byte[] sNetUser = new byte[MAX_NAMELEN];//网络操作的用户名 + public NET_DVR_IPADDR struRemoteHostAddr;//??程主机地址 + public int dwParaType;//参数类型 + public int dwChannel;//通道号 + public int dwDiskNumber;//硬盘号 + public int dwAlarmInPort;//报警输入端口 + public int dwAlarmOutPort;//报警输出端口 + public int dwInfoLen; + public byte[] sInfo = new byte[LOG_INFO_LEN]; +} + +//日志信息 +public static class NET_DVR_LOG extends Structure { + public NET_DVR_TIME strLogTime; + public int dwMajorType; //主类型 1-报警; 2-异常; 3-操作; 0xff-全部 + public int dwMinorType;//次类型 0-全部; + public byte[] sPanelUser = new byte[MAX_NAMELEN]; //操作面板的用户名 + public byte[] sNetUser = new byte[MAX_NAMELEN];//网络操作的用户名 + public byte[] sRemoteHostAddr = new byte[16];//远程主机地址 + public int dwParaType;//参数类型 + public int dwChannel;//通道号 + public int dwDiskNumber;//硬盘号 + public int dwAlarmInPort;//报警输入端口 + public int dwAlarmOutPort;//报警输出端口 +} + +/************************DVR日志 end***************************/ +public static class NET_DVR_ALARMOUTSTATUS_V30 extends Structure {//报警输出状态(9000扩展) + public byte[] Output = new byte[MAX_ALARMOUT_V30]; +} + +public static class NET_DVR_ALARMOUTSTATUS extends Structure {//报警输出状态 + public byte[] Output = new byte[MAX_ALARMOUT]; +} + +public static class NET_DVR_TRADEINFO extends Structure {//交易信息 + public short m_Year; + public short m_Month; + public short m_Day; + public short m_Hour; + public short m_Minute; + public short m_Second; + public byte[] DeviceName = new byte[24]; //设备名称 + public int dwChannelNumer; //通道号 + public byte[] CardNumber = new byte[32]; //卡号 + public byte[] cTradeType = new byte[12]; //交易类型 + public int dwCash; //交易金额 +} + +public static class NET_DVR_FRAMETYPECODE extends Structure {/*帧格式*/ + public byte[] code = new byte[12]; /* 代码 */ +} + +public static class NET_DVR_FRAMEFORMAT_V30 extends Structure {//ATM参数(9000扩展) + public int dwSize; + public NET_DVR_IPADDR struATMIP; /* ATM IP地址 */ + public int dwATMType; /* ATM类型 */ + public int dwInputMode; /* 输入方式 0-网络侦听 1-网络接收 2-串口直接输入 3-串口ATM命令输入*/ + public int dwFrameSignBeginPos; /* 报文标志位的起始位置*/ + public int dwFrameSignLength; /* 报文标志位的长度 */ + public byte[] byFrameSignContent = new byte[12]; /* 报文标志位的内容 */ + public int dwCardLengthInfoBeginPos; /* 卡号长度信息的起始位置 */ + public int dwCardLengthInfoLength; /* 卡号长度信息的长度 */ + public int dwCardNumberInfoBeginPos; /* 卡号信息的起始位置 */ + public int dwCardNumberInfoLength; /* 卡号信息的长度 */ + public int dwBusinessTypeBeginPos; /* 交易类型的起始位置 */ + public int dwBusinessTypeLength; /* 交易类型的长度 */ + public NET_DVR_FRAMETYPECODE[] frameTypeCode = new NET_DVR_FRAMETYPECODE[10]; /* 类型 */ + public short wATMPort; /* 卡号捕捉端口号(网络协议方式) (保留)0xffff表示该值无效*/ + public short wProtocolType; /* 网络协议类型(保留) 0xffff表示该值无效*/ + public byte[] byRes = new byte[24]; +} + +public static class NET_DVR_FRAMEFORMAT extends Structure {//ATM参数 + public int dwSize; + public byte[] sATMIP = new byte[16]; /* ATM IP地址 */ + public int dwATMType; /* ATM类型 */ + public int dwInputMode; /* 输入方式 0-网络侦听 1-网络接收 2-串口直接输入 3-串口ATM命令输入*/ + public int dwFrameSignBeginPos; /* 报文标志位的起始位置*/ + public int dwFrameSignLength; /* 报文标志位的长度 */ + public byte[] byFrameSignContent = new byte[12]; /* 报文标志位的内容 */ + public int dwCardLengthInfoBeginPos; /* 卡号长度信息的起始位置 */ + public int dwCardLengthInfoLength; /* 卡号长度信息的长度 */ + public int dwCardNumberInfoBeginPos; /* 卡号信息的起始位置 */ + public int dwCardNumberInfoLength; /* 卡号信息的长度 */ + public int dwBusinessTypeBeginPos; /* 交易类型的起始位置 */ + public int dwBusinessTypeLength; /* 交易类型的长度 */ + public NET_DVR_FRAMETYPECODE[] frameTypeCode = new NET_DVR_FRAMETYPECODE[10];/* 类型 */ +} + +public static class NET_DVR_FTPTYPECODE extends Structure { + public byte[] sFtpType = new byte[32]; /*客户定义的操作类型*/ + public byte[] sFtpCode = new byte[8]; /*客户定义的操作类型的对应的码*/ +} + +public static class NET_DVR_FRAMEFORMAT_EX extends Structure {//ATM参数添加FTP上传参数, 俄罗斯银行定制, 2006-11-17 + public int dwSize; + public byte[] sATMIP = new byte[16]; /* ATM IP地址 */ + public int dwATMType; /* ATM类型 */ + public int dwInputMode; /* 输入方式 0-网络侦听 1-网络接收 2-串口直接输入 3-串口ATM命令输入*/ + public int dwFrameSignBeginPos; /* 报文标志位的起始位置*/ + public int dwFrameSignLength; /* 报文标志位的长度 */ + public byte[] byFrameSignContent = new byte[12]; /* 报文标志位的内容 */ + public int dwCardLengthInfoBeginPos; /* 卡号长度信息的起始位置 */ + public int dwCardLengthInfoLength; /* 卡号长度信息的长度 */ + public int dwCardNumberInfoBeginPos; /* 卡号信息的起始位置 */ + public int dwCardNumberInfoLength; /* 卡号信息的长度 */ + public int dwBusinessTypeBeginPos; /* 交易类型的起始位置 */ + public int dwBusinessTypeLength; /* 交易类型的长度 */ + public NET_DVR_FRAMETYPECODE[] frameTypeCode = new NET_DVR_FRAMETYPECODE[10];/* 类型 */ + public byte[] sFTPIP = new byte[16]; /* FTP IP */ + public byte[] byFtpUsername = new byte[NAME_LEN]; /* 用户名 */ + public byte[] byFtpPasswd = new byte[PASSWD_LEN]; /* 密码 */ + public byte[] sDirName = new byte[NAME_LEN]; /*服务器目录名*/ + public int dwATMSrvType; /*ATM服务器类型,0--wincor ,1--diebold*/ + public int dwTimeSpace; /*取值为1.2.3.4.5.10*/ + public NET_DVR_FTPTYPECODE[] sFtpTypeCodeOp = new NET_DVR_FTPTYPECODE[300]; /*新加的*/ + public int dwADPlay; /* 1 表示在播放广告,0 表示没有播放广告*/ + public int dwNewPort; //端口 +} +/****************************ATM(end)***************************/ + +/*****************************DS-6001D/F(begin)***************************/ +//DS-6001D Decoder +public static class NET_DVR_DECODERINFO extends Structure { + public byte[] byEncoderIP = new byte[16]; //解码设备连接的服务器IP + public byte[] byEncoderUser = new byte[16]; //解码设备连接的服务器的用户名 + public byte[] byEncoderPasswd = new byte[16]; //解码设备连接的服务器的密码 + public byte bySendMode; //解码设备连接服务器的连接模式 + public byte byEncoderChannel; //解码设备连接的服务器的通道号 + public short wEncoderPort; //解码设备连接的服务器的端口号 + public byte[] reservedData = new byte[4]; //保留 +} + +public static class NET_DVR_DECODERSTATE extends Structure { + public byte[] byEncoderIP = new byte[16]; //解码设备连接的服务器IP + public byte[] byEncoderUser = new byte[16]; //解码设备连接的服务器的用户名 + public byte[] byEncoderPasswd = new byte[16]; //解码设备连接的服务器的密码 + public byte byEncoderChannel; //解码设备连接的服务器的通道号 + public byte bySendMode; //解码设备连接的服务器的连接模式 + public short wEncoderPort; //解码设备连接的服务器的端口号 + public int dwConnectState; //解码设备连接服务器的状态 + public byte[] reservedData = new byte[4]; //保留 +} + +public static class NET_DVR_DECCHANINFO extends Structure { + public byte[] sDVRIP = new byte[16]; /* DVR IP地址 */ + public short wDVRPort; /* 端口号 */ + public byte[] sUserName = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public byte byChannel; /* 通道号 */ + public byte byLinkMode; /* 连接模式 */ + public byte byLinkType; /* 连接类型 0-主码流 1-子码流 */ +} + +public static class NET_DVR_DECINFO extends Structure {/*每个解码通道的配置*/ + public byte byPoolChans; /*每路解码通道上的循环通道数量, 最多4通道 0表示没有解码*/ + public NET_DVR_DECCHANINFO[] struchanConInfo = new NET_DVR_DECCHANINFO[MAX_DECPOOLNUM]; + public byte byEnablePoll; /*是否轮巡 0-否 1-是*/ + public byte byPoolTime; /*轮巡时间 0-保留 1-10秒 2-15秒 3-20秒 4-30秒 5-45秒 6-1分钟 7-2分钟 8-5分钟 */ +} + +public static class NET_DVR_DECCFG extends Structure {/*整个设备解码配置*/ + public int dwSize; + public int dwDecChanNum; /*解码通道的数量*/ + public NET_DVR_DECINFO[] struDecInfo = new NET_DVR_DECINFO[MAX_DECNUM]; +} + +//2005-08-01 +public static class NET_DVR_PORTINFO extends Structure {/* 解码设备透明通道设置 */ + public int dwEnableTransPort; /* 是否启动透明通道 0-不启用 1-启用*/ + public byte[] sDecoderIP = new byte[16]; /* DVR IP地址 */ + public short wDecoderPort; /* 端口号 */ + public short wDVRTransPort; /* 配置前端DVR是从485/232输出,1表示232串口,2表示485串口 */ + public byte[] cReserve = new byte[4]; +} + +public static class NET_DVR_PORTCFG extends Structure { + public int dwSize; + public NET_DVR_PORTINFO[] struTransPortInfo = new NET_DVR_PORTINFO[MAX_TRANSPARENTNUM]; /* 数组0表示232 数组1表示485 */ +} + +/*https://jna.dev.java.net/javadoc/com/sun/jna/Union.html#setType(java.lang.Class) see how to use the JNA Union*/ +public static class NET_DVR_PLAYREMOTEFILE extends Structure {/* 控制网络文件回放 */ + public int dwSize; + public byte[] sDecoderIP = new byte[16]; /* DVR IP地址 */ + public short wDecoderPort; /* 端口号 */ + public short wLoadMode; /* 回放下载模式 1-按名字 2-按时间 */ + public byte[] byFile = new byte[100]; + public static class mode_size extends Union + { + public byte[] byFile = new byte[100]; // 回放的文件名 + public static class bytime extends Structure + { + public int dwChannel; + public byte[] sUserName = new byte[NAME_LEN]; //请求视频用户名 + public byte[] sPassword = new byte[PASSWD_LEN]; // 密码 + public NET_DVR_TIME struStartTime; //按时间回放的开始时间 + public NET_DVR_TIME struStopTime; // 按时间回放的结束时间 + } + } +} + +public static class NET_DVR_DECCHANSTATUS extends Structure {/*当前设备解码连接状态*/ + public int dwWorkType; /*工作方式:1:轮巡、2:动态连接解码、3:文件回放下载 4:按时间回放下载*/ + public byte[] sDVRIP = new byte[16]; /*连接的设备ip*/ + public short wDVRPort; /*连接端口号*/ + public byte byChannel; /* 通道号 */ + public byte byLinkMode; /* 连接模式 */ + public int dwLinkType; /*连接类型 0-主码流 1-子码流*/ + public byte[] sUserName = new byte[NAME_LEN]; /*请求视频用户名*/ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public byte[] cReserve = new byte[52]; + public static class objectInfo extends Union + { + public static class userInfo extends Structure + { + public byte[] sUserName = new byte[NAME_LEN]; //请求视频用户名 + public byte[] sPassword = new byte[PASSWD_LEN]; // 密码 + public byte[] cReserve = new byte[52]; + } + public static class fileInfo extends Structure + { + public byte[] fileName = new byte[100]; + } + public static class timeInfo extends Structure + { + public int dwChannel; + public byte[] sUserName = new byte[NAME_LEN]; //请求视频用户名 + public byte[] sPassword = new byte[PASSWD_LEN]; // 密码 + public NET_DVR_TIME struStartTime; // 按时间回放的开始时间 + public NET_DVR_TIME struStopTime; //按时间回放的结束时间 + } + } +} + +public static class NET_DVR_DECSTATUS extends Structure { + public int dwSize; + public NET_DVR_DECCHANSTATUS[] struDecState = new NET_DVR_DECCHANSTATUS[MAX_DECNUM]; +} +/*****************************DS-6001D/F(end)***************************/ + +public static class NET_DVR_SHOWSTRINGINFO extends Structure {//单字符参数(子结构) + public short wShowString; // 预览的图象上是否显示字符,0-不显示,1-显示 区域大小704*576,单个字符的大小为32*32 + public short wStringSize; /* 该行字符的长度,不能大于44个字符 */ + public short wShowStringTopLeftX; /* 字符显示位置的x坐标 */ + public short wShowStringTopLeftY; /* 字符名称显示位置的y坐标 */ + public byte[] sString = new byte[44]; /* 要显示的字符内容 */ +} + +//叠加字符(9000扩展) +public static class NET_DVR_SHOWSTRING_V30 extends Structure { + public int dwSize; + public NET_DVR_SHOWSTRINGINFO[] struStringInfo = new NET_DVR_SHOWSTRINGINFO[MAX_STRINGNUM_V30]; /* 要显示的字符内容 */ +} + +//叠加字符扩展(8条字符) +public static class NET_DVR_SHOWSTRING_EX extends Structure { + public int dwSize; + public NET_DVR_SHOWSTRINGINFO[] struStringInfo = new NET_DVR_SHOWSTRINGINFO[MAX_STRINGNUM_EX]; /* 要显示的字符内容 */ +} + +//叠加字符 +public static class NET_DVR_SHOWSTRING extends Structure { + public int dwSize; + public NET_DVR_SHOWSTRINGINFO[] struStringInfo = new NET_DVR_SHOWSTRINGINFO[MAX_STRINGNUM]; /* 要显示的字符内容 */ +} + +/****************************DS9000新增结构(begin)******************************/ + +/* +EMAIL参数结构 +*/ + public static class NET_DVR_SENDER extends Structure { + public byte[] sName = new byte[NAME_LEN]; /* 发件人姓名 */ + public byte[] sAddress = new byte[MAX_EMAIL_ADDR_LEN]; /* 发件人地址 */ + } + public static class NET_DVRRECEIVER extends Structure { + public byte[] sName = new byte[NAME_LEN]; /* 收件人姓名 */ + public byte[] sAddress = new byte[MAX_EMAIL_ADDR_LEN]; /* 收件人地址 */ + } + + public static class NET_DVR_EMAILCFG_V30 extends Structure { + public int dwSize; + public byte[] sAccount = new byte[NAME_LEN]; /* 账号*/ + public byte[] sPassword = new byte[MAX_EMAIL_PWD_LEN]; /*密码 */ + public NET_DVR_SENDER struSender; + public byte[] sSmtpServer = new byte[MAX_EMAIL_ADDR_LEN]; /* smtp服务器 */ + public byte[] sPop3Server = new byte[MAX_EMAIL_ADDR_LEN]; /* pop3服务器 */ + public NET_DVRRECEIVER[] struReceiver = new NET_DVRRECEIVER[3]; /* 最多可以设置3个收件人 */ + public byte byAttachment; /* 是否带附件 */ + public byte bySmtpServerVerify; /* 发送服务器要求身份验证 */ + public byte byMailInterval; /* mail interval */ + public byte[] res = new byte[77]; +} + +/* +DVR实现巡航数据结构 +*/ + public static class NET_DVR_CRUISE_PARA extends Structure { + public int dwSize; + public byte[] byPresetNo = new byte[CRUISE_MAX_PRESET_NUMS]; /* 预置点号 */ + public byte[] byCruiseSpeed = new byte[CRUISE_MAX_PRESET_NUMS]; /* 巡航速度 */ + public short[] wDwellTime = new short[CRUISE_MAX_PRESET_NUMS]; /* 停留时间 */ + public byte[] byEnableThisCruise; /* 是否启用 */ + public byte[] res = new byte[15]; +} + + /****************************DS9000新增结构(end)******************************/ + +//时间点 + public static class NET_DVR_TIMEPOINT extends Structure { + public int dwMonth; //月 0-11表示1-12个月 + public int dwWeekNo; //第几周 0-第1周 1-第2周 2-第3周 3-第4周 4-最后一周 + public int dwWeekDate; //星期几 0-星期日 1-星期一 2-星期二 3-星期三 4-星期四 5-星期五 6-星期六 + public int dwHour; //小时 开始时间0-23 结束时间1-23 + public int dwMin; //分 0-59 +} + +//夏令时参数 + public static class NET_DVR_ZONEANDDST extends Structure { + public int dwSize; + public byte[] byRes1 = new byte[16]; //保留 + public int dwEnableDST; //是否启用夏时制 0-不启用 1-启用 + public byte byDSTBias; //夏令时偏移值,30min, 60min, 90min, 120min, 以分钟计,传递原始数值 + public byte[] byRes2 = new byte[3]; + public NET_DVR_TIMEPOINT struBeginPoint; //夏时制开始时间 + public NET_DVR_TIMEPOINT struEndPoint; //夏时制停止时间 +} + +//图片质量 + public static class NET_DVR_JPEGPARA extends Structure { + /*注意:当图像压缩分辨率为VGA时,支持0=CIF, 1=QCIF, 2=D1抓图, + 当分辨率为3=UXGA(1600x1200), 4=SVGA(800x600), 5=HD720p(1280x720),6=VGA,7=XVGA, 8=HD900p + 仅支持当前分辨率的抓图*/ + public short wPicSize; /* 0=CIF, 1=QCIF, 2=D1 3=UXGA(1600x1200), 4=SVGA(800x600), 5=HD720p(1280x720),6=VGA*/ + public short wPicQuality; /* 图片质量系数 0-最好 1-较好 2-一般 */ + } + +/* aux video out parameter */ +//辅助输出参数配置 + public static class NET_DVR_AUXOUTCFG extends Structure { + public int dwSize; + public int dwAlarmOutChan; /* 选择报警弹出大报警通道切换时间:1画面的输出通道: 0:主输出/1:辅1/2:辅2/3:辅3/4:辅4 */ + public int dwAlarmChanSwitchTime; /* :1秒 - 10:10秒 */ + public int[] dwAuxSwitchTime = new int[MAX_AUXOUT]; /* 辅助输出切换时间: 0-不切换,1-5s,2-10s,3-20s,4-30s,5-60s,6-120s,7-300s */ + public byte[][] byAuxOrder = new byte[MAX_AUXOUT][MAX_WINDOW]; /* 辅助输出预览顺序, 0xff表示相应的窗口不预览 */ +} + +//ntp + public static class NET_DVR_NTPPARA extends Structure { + public byte[] sNTPServer = new byte[64]; /* Domain Name or IP addr of NTP server */ + public short wInterval; /* adjust time interval(hours) */ + public byte byEnableNTP; /* enable NPT client 0-no,1-yes*/ + public byte cTimeDifferenceH; /* 与国际标准时间的 小时偏移-12 ... +13 */ + public byte cTimeDifferenceM;/* 与国际标准时间的 分钟偏移0, 30, 45*/ + public byte res1; + public short wNtpPort; /* ntp server port 9000新增 设备默认为123*/ + public byte[] res2 = new byte[8]; +} + +//ddns + public static class NET_DVR_DDNSPARA extends Structure { + public byte[] sUsername = new byte[NAME_LEN]; /* DDNS账号用户名/密码 */ + public byte[] sPassword = new byte[PASSWD_LEN]; + public byte[] sDomainName = new byte[64]; /* 域名 */ + public byte byEnableDDNS; /*是否应用 0-否,1-是*/ + public byte[] res = new byte[15]; +} + + public static class NET_DVR_DDNSPARA_EX extends Structure { + public byte byHostIndex; /* 0-Hikvision DNS 1-Dyndns 2-PeanutHull(花生壳), 3-希网3322*/ + public byte byEnableDDNS; /*是否应用DDNS 0-否,1-是*/ + public short wDDNSPort; /* DDNS端口号 */ + public byte[] sUsername = new byte[NAME_LEN]; /* DDNS用户名*/ + public byte[] sPassword = new byte[PASSWD_LEN]; /* DDNS密码 */ + public byte[] sDomainName = new byte[MAX_DOMAIN_NAME]; /* 设备配备的域名地址 */ + public byte[] sServerName = new byte[MAX_DOMAIN_NAME]; /* DDNS 对应的服务器地址,可以是IP地址或域名 */ + public byte[] byRes = new byte[16]; +} + + public static class NET_DVR_DDNS extends Structure { + public byte[] sUsername = new byte[NAME_LEN]; /* DDNS账号用户名*/ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public byte[] sDomainName = new byte[MAX_DOMAIN_NAME]; /* 设备配备的域名地址 */ + public byte[] sServerName = new byte[MAX_DOMAIN_NAME]; /* DDNS协议对应的服务器地址,可以是IP地址或域名 */ + public short wDDNSPort; /* 端口号 */ + public byte[] byRes = new byte[10]; + } +//9000扩展 +public static class NET_DVR_DDNSPARA_V30 extends Structure { + public byte byEnableDDNS; + public byte byHostIndex;/* 0-Hikvision DNS(保留) 1-Dyndns 2-PeanutHull(花生壳) 3-希网3322 */ + public byte[] byRes1 = new byte[2]; + public NET_DVR_DDNS[] struDDNS = new NET_DVR_DDNS[MAX_DDNS_NUMS];//9000目前只支持前3个配置,其他配置保留 + public byte[] byRes2 = new byte[16]; +} + +//email +public static class NET_DVR_EMAILPARA extends Structure { + public byte[] sUsername = new byte[64]; /* 邮件账号/密码 */ + public byte[] sPassword = new byte[64]; + public byte[] sSmtpServer = new byte[64]; + public byte[] sPop3Server = new byte[64]; + public byte[] sMailAddr = new byte[64]; /* email */ + public byte[] sEventMailAddr1 = new byte[64]; /* 上传报警/异常等的email */ + public byte[] sEventMailAddr2 = new byte[64]; + public byte[] res = new byte[16]; +} + +public static class NET_DVR_NETAPPCFG extends Structure {//网络参数配置 + public int dwSize; + public byte[] sDNSIp = new byte[16]; /* DNS服务器地址 */ + public NET_DVR_NTPPARA struNtpClientParam; /* NTP参数 */ + public NET_DVR_DDNSPARA struDDNSClientParam; /* DDNS参数 */ + //NET_DVR_EMAILPARA struEmailParam; /* EMAIL参数 */ + public byte[] res = new byte[464]; /* 保留 */ +} + +public static class NET_DVR_SINGLE_NFS extends Structure {//nfs结构配置 + public byte[] sNfsHostIPAddr = new byte[16]; + public byte[] sNfsDirectory = new byte[PATHNAME_LEN]; // PATHNAME_LEN = 128 +} + +public static class NET_DVR_NFSCFG extends Structure { + public int dwSize; + public NET_DVR_SINGLE_NFS[] struNfsDiskParam = new NET_DVR_SINGLE_NFS[MAX_NFS_DISK]; +} + +//巡航点配置(HIK IP快球专用) +public static class NET_DVR_CRUISE_POINT extends Structure { + public byte PresetNum; //预置点 + public byte Dwell; //停留时间 + public byte Speed; //速度 + public byte Reserve; //保留 +} + +public static class NET_DVR_CRUISE_RET extends Structure { + public NET_DVR_CRUISE_POINT[] struCruisePoint = new NET_DVR_CRUISE_POINT[32]; //最大支持32个巡航点 +} + +/************************************多路解码器(begin)***************************************/ +//多路解码器扩展 added by zxy 2007-05-23 +public static class NET_DVR_NETCFG_OTHER extends Structure { + public int dwSize; + public byte[] sFirstDNSIP = new byte[16]; + public byte[] sSecondDNSIP = new byte[16]; + public byte[] sRes = new byte[32]; +} + +public static class NET_DVR_MATRIX_DECINFO extends Structure { + public byte[] sDVRIP = new byte[16]; /* DVR IP地址 */ + public short wDVRPort; /* 端口号 */ + public byte byChannel; /* 通道号 */ + public byte byTransProtocol; /* 传输协议类型 0-TCP 1-UDP */ + public byte byTransMode; /* 传输码流模式 0-主码流 1-子码流*/ + public byte[] byRes = new byte[3]; + public byte[] sUserName = new byte[NAME_LEN]; /* 监控主机登陆帐号 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 监控主机密码 */ +} + +public static class NET_DVR_MATRIX_DYNAMIC_DEC extends Structure {//启动/停止动态解码 + public int dwSize; + public NET_DVR_MATRIX_DECINFO struDecChanInfo; /* 动态解码通道信息 */ +} + +public static class NET_DVR_MATRIX_DEC_CHAN_STATUS extends Structure {//2007-12-13 modified by zxy 修改多路解码器的NET_DVR_MATRIX_DEC_CHAN_STATUS结构 + public int dwSize;//2008-1-16 modified by zxy dwIsLinked的状态由原来的0-未链接 1-连接修改成以下三种状态。 + public int dwIsLinked; /* 解码通道状态 0-休眠 1-正在连接 2-已连接 3-正在解码 */ + public int dwStreamCpRate; /* Stream copy rate, X kbits/second */ + public byte[] cRes = new byte[64]; /* 保留 */ +} +//end 2007-12-13 modified by zxy + +public static class NET_DVR_MATRIX_DEC_CHAN_INFO extends Structure { + public int dwSize; + public NET_DVR_MATRIX_DECINFO struDecChanInfo; /* 解码通道信息 */ + public int dwDecState; /* 0-动态解码 1-循环解码 2-按时间回放 3-按文件回放 */ + public NET_DVR_TIME StartTime; /* 按时间回放开始时间 */ + public NET_DVR_TIME StopTime; /* 按时间回放停止时间 */ + public byte[] sFileName = new byte[128]; /* 按文件回放文件名 */ +} + +//连接的通道配置 2007-11-05 +public static class NET_DVR_MATRIX_DECCHANINFO extends Structure { + public int dwEnable; /* 是否启用 0-否 1-启用*/ + public NET_DVR_MATRIX_DECINFO struDecChanInfo; /* 轮循解码通道信息 */ +} + +//2007-11-05 新增每个解码通道的配置 +public static class NET_DVR_MATRIX_LOOP_DECINFO extends Structure { + public int dwSize; + public int dwPoolTime; /*轮巡时间 */ + public NET_DVR_MATRIX_DECCHANINFO[] struchanConInfo = new NET_DVR_MATRIX_DECCHANINFO[MAX_CYCLE_CHAN]; +} + +//2007-05-25 多路解码器数字矩阵配置 +//矩阵行信息 2007-12-28 +public static class NET_DVR_MATRIX_ROW_ELEMENT extends Structure { + public byte[] sSurvChanName = new byte[128]; /* 监控通道名称,支持中文 */ + public int dwRowNum; /* 行号 */ + public NET_DVR_MATRIX_DECINFO struDecChanInfo; /* 矩阵行信息 */ +} + +public static class NET_DVR_MATRIX_ROW_INDEX extends Structure { + public byte[] sSurvChanName = new byte[128]; /* 监控通道名称,支持中文 */ + public int dwRowNum; /* 行号 */ +} + +//矩阵列信息 2007-12-28 +public static class NET_DVR_MATRIX_COLUMN_ELEMENT extends Structure { + public int dwLocalDispChanNum; /* 本地显示通道号 */ + public int dwGlobalDispChanNum; /* 全局显示通道号 */ + public int dwRes; /* 保留 */ +} + +public static class NET_DVR_MATRIX_GLOBAL_COLUMN_ELEMENT extends Structure { + public int dwConflictTag; /* 冲突标记,0:无冲突,1:冲突 */ + public int dwConflictGloDispChan; /* 与之冲突的全局通道号 */ + public NET_DVR_MATRIX_COLUMN_ELEMENT struColumnInfo;/* 矩阵列元素结构体 */ +} + +//手动查看 2007-12-28 +public static class NET_DVR_MATRIX_ROW_COLUMN_LINK extends Structure { + public int dwSize; + /* + * 以下三个参数只需要指定其中一个便可指定数字矩阵里的某一行 + * 所代表的远程监控通道。 + * 如果指定了多个域并有冲突,设备将按照域的先后顺序为准取最先定义者。 + */ + public int dwRowNum; /* -1代表无效域,大于0者方为有效的矩阵行号 */ + public byte[] sSurvChanName = new byte[128]; /* 监控通道名,是否无效按字符串的有效性判断 */ + public int dwSurvNum; /* 监控通道号,按矩阵行列表的顺序指定,一般情况下与行号一致 */ + /* + * 以下两项只需要指定其中一项便可,如果两项都有效默认选择第一项 + */ + public int dwGlobalDispChanNum; /* 电视墙上的电视机编号 */ + public int dwLocalDispChanNum; + /* + * 0代表播放即时码流, + * 1表示按时间回访远程监控设备的文件 + * 2表示按文件名回访 + */ + public int dwTimeSel; + public NET_DVR_TIME StartTime; + public NET_DVR_TIME StopTime; + public byte[] sFileName = new byte[128]; +} + +public static class NET_DVR_MATRIX_PREVIEW_DISP_CHAN extends Structure { + public int dwSize; + public int dwGlobalDispChanNum; /* 电视墙上的电视机编号 */ + public int dwLocalDispChanNum; /* 解码通道 */ +} + +public static class NET_DVR_MATRIX_LOOP_PLAY_SET extends Structure {//轮循功能 2007-12-28 + public int dwSize; + /* 任意指定一个,-1为无效,如果都指定则以LocalDispChanNum为准 */ + public int dwLocalDispChanNum; /* 解码通道 */ + public int dwGlobalDispChanNum; /* 电视墙上的电视机编号 */ + public int dwCycTimeInterval; /* 轮循时间间隔 */ +} + +public static class NET_DVR_MATRIX_LOCAL_HOST_INFO extends Structure {//矩阵中心配置 2007-12-28 + public int dwSize; + public int dwLocalHostProperty; /* 本地主机类型 0-服务器 1-客户端*/ + public int dwIsIsolated; /* 本地主机是否独立于系统,0:联网,1:独立 */ + public int dwLocalMatrixHostPort; /* 本地主机访问端口 */ + public byte[] byLocalMatrixHostUsrName = new byte[NAME_LEN]; /* 本地主机登录用户名 */ + public byte[] byLocalMatrixHostPasswd = new byte[PASSWD_LEN]; /* 本地主机登录密码 */ + public int dwLocalMatrixCtrlMedia; /* 控制方式 0x1串口键盘控制 0x2网络键盘控制 0x4矩阵中心控制 0x8PC客户端控制*/ + public byte[] sMatrixCenterIP = new byte[16]; /* 矩阵中心IP地址 */ + public int dwMatrixCenterPort; /* 矩阵中心端口号 */ + public byte[] byMatrixCenterUsrName = new byte[NAME_LEN]; /* 矩阵中心登录用户名 */ + public byte[] byMatrixCenterPasswd = new byte[PASSWD_LEN]; /* 矩阵中心登录密码 */ +} + +//2007-12-22 +public static class TTY_CONFIG extends Structure { + public byte baudrate; /* 波特率 */ + public byte databits; /* 数据位 */ + public byte stopbits; /* 停止位 */ + public byte parity; /* 奇偶校验位 */ + public byte flowcontrol; /* 流控 */ + public byte[] res = new byte[3]; +} + +public static class NET_DVR_MATRIX_TRAN_CHAN_INFO extends Structure { + public byte byTranChanEnable; /* 当前透明通道是否打开 0:关闭 1:打开 */ + /* + * 多路解码器本地有1个485串口,1个232串口都可以作为透明通道,设备号分配如下: + * 0 RS485 + * 1 RS232 Console + */ + public byte byLocalSerialDevice; /* Local serial device */ + /* + * 远程串口输出还是两个,一个RS232,一个RS485 + * 1表示232串口 + * 2表示485串口 + */ + public byte byRemoteSerialDevice; /* Remote output serial device */ + public byte res1; /* 保留 */ + public byte[] sRemoteDevIP= new byte[16]; /* Remote Device IP */ + public short wRemoteDevPort; /* Remote Net Communication Port */ + public byte[] res2= new byte[2]; /* 保留 */ + public TTY_CONFIG RemoteSerialDevCfg; +} + +public static class NET_DVR_MATRIX_TRAN_CHAN_CONFIG extends Structure { + public int dwSize; + public byte by232IsDualChan; /* 设置哪路232透明通道是全双工的 取值1到MAX_SERIAL_NUM */ + public byte by485IsDualChan; /* 设置哪路485透明通道是全双工的 取值1到MAX_SERIAL_NUM */ + public byte[] res = new byte[2]; /* 保留 */ + public NET_DVR_MATRIX_TRAN_CHAN_INFO[] struTranInfo = new NET_DVR_MATRIX_TRAN_CHAN_INFO[MAX_SERIAL_NUM];/*同时支持建立MAX_SERIAL_NUM个透明通道*/ +} + +//2007-12-24 Merry Christmas Eve... +public static class NET_DVR_MATRIX_DEC_REMOTE_PLAY extends Structure { + public int dwSize; + public byte[] sDVRIP = new byte[16]; /* DVR IP地址 */ + public short wDVRPort; /* 端口号 */ + public byte byChannel; /* 通道号 */ + public byte byReserve; + public byte[] sUserName = new byte[NAME_LEN]; /* 用户名 */ + public byte[] sPassword = new byte[PASSWD_LEN]; /* 密码 */ + public int dwPlayMode; /* 0-按文件 1-按时间*/ + public NET_DVR_TIME StartTime; + public NET_DVR_TIME StopTime; + public byte[] sFileName = new byte[128]; +} + + +public static class NET_DVR_MATRIX_DEC_REMOTE_PLAY_CONTROL extends Structure { + public int dwSize; + public int dwPlayCmd; /* 播放命令 见文件播放命令*/ + public int dwCmdParam; /* 播放命令参数 */ +} + +public static class NET_DVR_MATRIX_DEC_REMOTE_PLAY_STATUS extends Structure { + public int dwSize; + public int dwCurMediaFileLen; /* 当前播放的媒体文件长度 */ + public int dwCurMediaFilePosition; /* 当前播放文件的播放位置 */ + public int dwCurMediaFileDuration; /* 当前播放文件的总时间 */ + public int dwCurPlayTime; /* 当前已经播放的时间 */ + public int dwCurMediaFIleFrames; /* 当前播放文件的总帧数 */ + public int dwCurDataType; /* 当前传输的数据类型,19-文件头,20-流数据, 21-播放结束标志 */ + public byte[] res = new byte[72]; +} + +public static class NET_DVR_MATRIX_PASSIVEMODE extends Structure { + public short wTransProtol; //传输协议,0-TCP, 1-UDP, 2-MCAST + public short wPassivePort; //TCP,UDP时为TCP,UDP端口, MCAST时为MCAST端口 + public byte[] sMcastIP = new byte[16]; //TCP,UDP时无效, MCAST时为多播地址 + public byte[] res = new byte[8]; +} +/************************************多路解码器(end)***************************************/ + +/************************************多路解码器(end)***************************************/ + +public static class NET_DVR_EMAILCFG extends Structure +{ /* 12 bytes */ + public int dwSize; + public byte[] sUserName = new byte[32]; + public byte[] sPassWord = new byte[32]; + public byte[] sFromName = new byte[32]; /* Sender *///字符串中的第一个字符和最后一个字符不能是"@",并且字符串中要有"@"字符 + public byte[] sFromAddr = new byte[48]; /* Sender address */ + public byte[] sToName1 = new byte[32]; /* Receiver1 */ + public byte[] sToName2 = new byte[32]; /* Receiver2 */ + public byte[] sToAddr1 = new byte[48]; /* Receiver address1 */ + public byte[] sToAddr2 = new byte[48]; /* Receiver address2 */ + public byte[] sEmailServer = new byte[32]; /* Email server address */ + public byte byServerType; /* Email server type: 0-SMTP, 1-POP, 2-IMTP…*/ + public byte byUseAuthen; /* Email server authentication method: 1-enable, 0-disable */ + public byte byAttachment; /* enable attachment */ + public byte byMailinterval; /* mail interval 0-2s, 1-3s, 2-4s. 3-5s*/ +} + +public static class NET_DVR_COMPRESSIONCFG_NEW extends Structure +{ + public int dwSize; + public NET_DVR_COMPRESSION_INFO_EX struLowCompression; //定时录像 + public NET_DVR_COMPRESSION_INFO_EX struEventCompression; //事件触发录像 +} + +//球机位置信息 +public static class NET_DVR_PTZPOS extends Structure +{ + public short wAction;//获取时该字段无效 + public short wPanPos;//水平参数 + public short wTiltPos;//垂直参数 + public short wZoomPos;//变倍参数 +} + +//球机范围信息 +public static class NET_DVR_PTZSCOPE extends Structure +{ + public short wPanPosMin;//水平参数min + public short wPanPosMax;//水平参数max + public short wTiltPosMin;//垂直参数min + public short wTiltPosMax;//垂直参数max + public short wZoomPosMin;//变倍参数min + public short wZoomPosMax;//变倍参数max +} + +public static class NET_DVR_PTZABSOLUTEEX_CFG extends Structure +{ + public int dwSize;//结构体大小 + public NET_PTZ_INFO struPTZCtrl = new NET_PTZ_INFO();//设备PTZF信息 + public int dwFocalLen;//焦距范围:0-100000MM + public float fHorizontalSpeed;//水平转动速度:0.01-1000.00度/S + public float fVerticalSpeed;//垂直转动速度:0.01-1000.00度/S + /*镜头变倍配置类型;absoluteZoom:通过变倍参数进行配置,选择为该类型时struPTZCtrl中的fZoom参数生效。focalLen:通过焦距参数进行配置,选择为该类型时,dwFocalLen参数生效。*/ + public byte byZoomType;// 镜头变倍配置类型0~ absoluteZoom,1~ focalLen + public byte[] byRes = new byte[123]; +} + +//rtsp配置 ipcamera专用 +public static class NET_DVR_RTSPCFG extends Structure +{ + public int dwSize; //长度 + public short wPort; //rtsp服务器侦听端口 + public byte[] byReserve = new byte[54]; //预留 +} + +/********************************接口参数结构(begin)*********************************/ + +//NET_DVR_Login()参数结构 +public static class NET_DVR_DEVICEINFO extends Structure +{ + public byte[] sSerialNumber = new byte[SERIALNO_LEN]; //序列号 + public byte byAlarmInPortNum; //DVR报警输入个数 + public byte byAlarmOutPortNum; //DVR报警输出个数 + public byte byDiskNum; //DVR硬盘个数 + public byte byDVRType; //DVR类型, 1:DVR 2:ATM DVR 3:DVS ...... + public byte byChanNum; //DVR 通道个数 + public byte byStartChan; //起始通道号,例如DVS-1,DVR - 1 +} + +//NET_DVR_Login_V30()参数结构 +public static class NET_DVR_DEVICEINFO_V30 extends Structure +{ + public byte[] sSerialNumber = new byte[SERIALNO_LEN]; //序列号 + public byte byAlarmInPortNum; //报警输入个数 + public byte byAlarmOutPortNum; //报警输出个数 + public byte byDiskNum; //硬盘个数 + public byte byDVRType; //设备类型, 1:DVR 2:ATM DVR 3:DVS ...... + public byte byChanNum; //模拟通道个数 + public byte byStartChan; //起始通道号,例如DVS-1,DVR - 1 + public byte byAudioChanNum; //语音通道数 + public byte byIPChanNum; //最大数字通道个数,低位 + public byte byZeroChanNum; //零通道编码个数 //2010-01-16 + public byte byMainProto; //主码流传输协议类型 0-private, 1-rtsp,2-同时支持private和rtsp + public byte bySubProto; //子码流传输协议类型0-private, 1-rtsp,2-同时支持private和rtsp + public byte bySupport; //能力,位与结果为0表示不支持,1表示支持, + public byte bySupport1; // 能力集扩充,位与结果为0表示不支持,1表示支持 + public byte bySupport2; /*能力*/ + public short wDevType; //设备型号 + public byte bySupport3; //能力集扩展 + public byte byMultiStreamProto;//是否支持多码流,按位表示,0-不支持,1-支持,bit1-码流3,bit2-码流4,bit7-主码流,bit-8子码流 + public byte byStartDChan; //起始数字通道号,0表示无效 + public byte byStartDTalkChan; //起始数字对讲通道号,区别于模拟对讲通道号,0表示无效 + public byte byHighDChanNum; //数字通道个数,高位 + public byte bySupport4; //能力集扩展 + public byte byLanguageType;// 支持语种能力,按位表示,每一位0-不支持,1-支持 + // byLanguageType 等于0 表示 老设备 + // byLanguageType & 0x1表示支持中文 + // byLanguageType & 0x2表示支持英文 + public byte byVoiceInChanNum; //音频输入通道数 + public byte byStartVoiceInChanNo; //音频输入起始通道号 0表示无效 + public byte bySupport5; + public byte bySupport6; //能力 + public byte byMirrorChanNum; //镜像通道个数,<录播主机中用于表示导播通道> + public short wStartMirrorChanNo; //起始镜像通道号 + public byte bySupport7; //能力 + public byte byRes2; //保留 +} + +public static final int NET_DVR_DEV_ADDRESS_MAX_LEN = 129; +public static final int NET_DVR_LOGIN_USERNAME_MAX_LEN = 64; +public static final int NET_DVR_LOGIN_PASSWD_MAX_LEN = 64; + + public static interface FLoginResultCallback extends Callback{ + public int invoke(int lUserID,int dwResult,NET_DVR_DEVICEINFO_V30 lpDeviceinfo,Pointer pUser); +} + +//NET_DVR_Login_V40()参数 +public static class NET_DVR_USER_LOGIN_INFO extends Structure +{ + public byte[] sDeviceAddress = new byte[NET_DVR_DEV_ADDRESS_MAX_LEN]; + public byte byUseTransport; + public short wPort; + public byte[] sUserName = new byte[NET_DVR_LOGIN_USERNAME_MAX_LEN]; + public byte[] sPassword = new byte[NET_DVR_LOGIN_PASSWD_MAX_LEN]; + public FLoginResultCallback cbLoginResult; + public Pointer pUser; + public boolean bUseAsynLogin; + public byte byProxyType; //0:不使用代理,1:使用标准代理,2:使用EHome代理 + public byte byUseUTCTime; //0-不进行转换,默认,1-接口上输入输出全部使用UTC时间,SDK完成UTC时间与设备时区的转换,2-接口上输入输出全部使用平台本地时间,SDK完成平台本地时间与设备时区的转换 + public byte byLoginMode; //0-Private 1-ISAPI 2-自适应 + public byte byHttps; //0-不适用tls,1-使用tls 2-自适应 + public int iProxyID; //代理服务器序号,添加代理服务器信息时,相对应的服务器数组下表值 + public byte byVerifyMode; //认证方式,0-不认证,1-双向认证,2-单向认证;认证仅在使用TLS的时候生效; + public byte[] byRes2 = new byte[119]; +} + +//NET_DVR_Login_V40()参数 +public static class NET_DVR_DEVICEINFO_V40 extends Structure +{ + public NET_DVR_DEVICEINFO_V30 struDeviceV30 = new NET_DVR_DEVICEINFO_V30(); + public byte bySupportLock; + public byte byRetryLoginTime; + public byte byPasswordLevel; + public byte byRes1; + public int dwSurplusLockTime; + public byte byCharEncodeType;//字符编码类型:0- 无字符编码信息(老设备),1- GB2312(简体中文),2- GBK,3- BIG5(繁体中文),4- Shift_JIS(日文),5- EUC-KR(韩文),6- UTF-8,7- ISO8859-1,8- ISO8859-2,9- ISO8859-3,…,依次类推,21- ISO8859-15(西欧) + public byte bySupportDev5; //支持v50版本的设备参数获取,设备名称和设备类型名称长度扩展为64字节 + public byte bySupport; //能力集扩展,位与结果:0- 不支持,1- 支持 + public byte byLoginMode; //登录模式 0-Private登录 1-ISAPI登录 + public int dwOEMCode; + public int iResidualValidity; //该用户密码剩余有效天数,单位:天,返回负值,表示密码已经超期使用,例如“-3表示密码已经超期使用3天” + public byte byResidualValidity; // iResidualValidity字段是否有效,0-无效,1-有效 + public byte bySingleStartDTalkChan; //独立音轨接入的设备,起始接入通道号,0-为保留字节,无实际含义,音轨通道号不能从0开始 + public byte bySingleDTalkChanNums; //独立音轨接入的设备的通道总数,0-表示不支持 + public byte byPassWordResetLevel; //0-无效,1-管理员创建一个非管理员用户为其设置密码,该非管理员用户正确登录设备后要提示“请修改初始登录密码”,未修改的情况下,用户每次登入都会进行提醒;2-当非管理员用户的密码被管理员修改,该非管理员用户再次正确登录设备后,需要提示“请重新设置登录密码”,未修改的情况下,用户每次登入都会进行提醒。 + public byte bySupportStreamEncrypt; //能力集扩展,位与结果:0- 不支持,1- 支持 bySupportStreamEncrypt & 0x1:表示是否支持RTP/TLS取流 bySupportStreamEncrypt & 0x2: 表示是否支持SRTP/UDP取流 bySupportStreamEncrypt & 0x4: 表示是否支持SRTP/MULTICAST取流 + public byte byMarketType;//0-无效(未知类型),1-经销型,2-行业型 + public byte[] byRes2 = new byte[238]; + + +} + +//sdk网络环境枚举变量,用于远程升级 + enum _SDK_NET_ENV +{ + LOCAL_AREA_NETWORK , + WIDE_AREA_NETWORK +} + +//显示模式 + enum DISPLAY_MODE +{ + NORMALMODE , + OVERLAYMODE +} + +//发送模式 + enum SEND_MODE +{ + PTOPTCPMODE, + PTOPUDPMODE, + MULTIMODE, + RTPMODE, + RESERVEDMODE +}; + +//抓图模式 + enum CAPTURE_MODE +{ + BMP_MODE, //BMP模式 + JPEG_MODE //JPEG模式 +}; + +//实时声音模式 + enum REALSOUND_MODE +{ + NONE, //SDK中无此模式,只是为了填补0这个位置 + MONOPOLIZE_MODE , //独占模式 1 + SHARE_MODE //共享模式 2 +}; + +//软解码预览参数 +public static class NET_DVR_CLIENTINFO extends Structure { + public int lChannel; + public int lLinkMode; + public int hPlayWnd; + public String sMultiCastIP; +} + +//预览V40接口 +public static class NET_DVR_PREVIEWINFO extends Structure { + public int lChannel;//通道号 + public int dwStreamType; // 码流类型,0-主码流,1-子码流,2-码流3,3-码流4, 4-码流5,5-码流6,7-码流7,8-码流8,9-码流9,10-码流10 + public int dwLinkMode;// 0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-RTP/RTSP,5-RSTP/HTTP ,6- HRUDP(可靠传输) ,7-RTSP/HTTPS + public int hPlayWnd;//播放窗口的句柄,为NULL表示不播放图象 + public int bBlocked; //0-非阻塞取流, 1-阻塞取流, 如果阻塞SDK内部connect失败将会有5s的超时才能够返回,不适合于轮询取流操作. + public int bPassbackRecord; //0-不启用录像回传,1启用录像回传 + public byte byPreviewMode;//预览模式,0-正常预览,1-延迟预览 + public byte[] byStreamID = new byte[32];//流ID,lChannel为0xffffffff时启用此参数 + public byte byProtoType; //应用层取流协议,0-私有协议,1-RTSP协议 + public byte byRes1; + public byte byVideoCodingType; //码流数据编解码类型 0-通用编码数据 1-热成像探测器产生的原始数据(温度数据的加密信息,通过去加密运算,将原始数据算出真实的温度值) + public int dwDisplayBufNum; //播放库播放缓冲区最大缓冲帧数,范围1-50,置0时默认为1 + public byte byNPQMode; //NPQ是直连模式,还是过流媒体 0-直连 1-过流媒体 + public byte[] byRes = new byte[215]; +} + +public static class NET_DVR_STREAM_INFO extends Structure + { + public int dwSize; + public byte[] byID = new byte[32]; + public int dwChannel; + public byte[] byRes = new byte[32]; + } + + public static class NET_DVR_STREAM_RECORD_STATUS extends Structure + { + public int dwSize; + public byte byRecord; + public byte byOffLineRecord; + public byte[] byRes1 = new byte[2]; + public int dwRelatedHD; + public byte[] byRes2 = new byte[8]; + } + +//SDK状态信息(9000新增) +public static class NET_DVR_SDKSTATE extends Structure +{ + public int dwTotalLoginNum; //当前login用户数 + public int dwTotalRealPlayNum; //当前realplay路数 + public int dwTotalPlayBackNum; //当前回放或下载路数 + public int dwTotalAlarmChanNum; //当前建立报警通道路数 + public int dwTotalFormatNum; //当前硬盘格式化路数 + public int dwTotalFileSearchNum; //当前日志或文件搜索路数 + public int dwTotalLogSearchNum; //当前日志或文件搜索路数 + public int dwTotalSerialNum; //当前透明通道路数 + public int dwTotalUpgradeNum; //当前升级路数 + public int dwTotalVoiceComNum; //当前语音转发路数 + public int dwTotalBroadCastNum; //当前语音广播路数 + public int[] dwRes = new int[10]; +} + +//SDK功能支持信息(9000新增) +public static class NET_DVR_SDKABL extends Structure +{ + public int dwMaxLoginNum; //最大login用户数 MAX_LOGIN_USERS + public int dwMaxRealPlayNum; //最大realplay路数 WATCH_NUM + public int dwMaxPlayBackNum; //最大回放或下载路数 WATCH_NUM + public int dwMaxAlarmChanNum; //最大建立报警通道路数 ALARM_NUM + public int dwMaxFormatNum; //最大硬盘格式化路数 SERVER_NUM + public int dwMaxFileSearchNum; //最大文件搜索路数 SERVER_NUM + public int dwMaxLogSearchNum; //最大日志搜索路数 SERVER_NUM + public int dwMaxSerialNum; //最大透明通道路数 SERVER_NUM + public int dwMaxUpgradeNum; //最大升级路数 SERVER_NUM + public int dwMaxVoiceComNum; //最大语音转发路数 SERVER_NUM + public int dwMaxBroadCastNum; //最大语音广播路数 MAX_CASTNUM + public int[] dwRes = new int[10]; +} + +//报警设备信息 +public static class NET_DVR_ALARMER extends Structure +{ + public byte byUserIDValid; /* userid是否有效 0-无效,1-有效 */ + public byte bySerialValid; /* 序列号是否有效 0-无效,1-有效 */ + public byte byVersionValid; /* 版本号是否有效 0-无效,1-有效 */ + public byte byDeviceNameValid; /* 设备名字是否有效 0-无效,1-有效 */ + public byte byMacAddrValid; /* MAC地址是否有效 0-无效,1-有效 */ + public byte byLinkPortValid; /* login端口是否有效 0-无效,1-有效 */ + public byte byDeviceIPValid; /* 设备IP是否有效 0-无效,1-有效 */ + public byte bySocketIPValid; /* socket ip是否有效 0-无效,1-有效 */ + public int lUserID; /* NET_DVR_Login()返回值, 布防时有效 */ + public byte[] sSerialNumber = new byte[SERIALNO_LEN]; /* 序列号 */ + public int dwDeviceVersion; /* 版本信息 高16位表示主版本,低16位表示次版本*/ + public byte[] sDeviceName = new byte[NAME_LEN]; /* 设备名字 */ + public byte[] byMacAddr = new byte[MACADDR_LEN]; /* MAC地址 */ + public short wLinkPort; /* link port */ + public byte[] sDeviceIP = new byte[128]; /* IP地址 */ + public byte[] sSocketIP = new byte[128]; /* 报警主动上传时的socket IP地址 */ + public byte byIpProtocol; /* Ip协议 0-IPV4, 1-IPV6 */ + public byte[] byRes2 = new byte[11]; + + +} + +//硬解码显示区域参数(子结构) +public static class NET_DVR_DISPLAY_PARA extends Structure +{ + public int bToScreen; + public int bToVideoOut; + public int nLeft; + public int nTop; + public int nWidth; + public int nHeight; + public int nReserved; +} + +//硬解码预览参数 +public static class NET_DVR_CARDINFO extends Structure +{ + public int lChannel;//通道号 + public int lLinkMode; //最高位(31)为0表示主码流,为1表示子,0-30位表示码流连接方式:0:TCP方式,1:UDP方式,2:多播方式,3 - RTP方式,4-电话线,5-128k宽带,6-256k宽带,7-384k宽带,8-512k宽带; + public String sMultiCastIP; + public NET_DVR_DISPLAY_PARA struDisplayPara; +} + +//录象文件参数 +public static class NET_DVR_FIND_DATA extends Structure +{ + public byte[] sFileName = new byte[100];//文件名 + public NET_DVR_TIME struStartTime;//文件的开始时间 + public NET_DVR_TIME struStopTime;//文件的结束时间 + public int dwFileSize;//文件的大小 +} + +//录象文件参数(9000) + public static class NET_DVR_FINDDATA_V30 extends Structure { + public byte[] sFileName = new byte[100];//文件名 + public NET_DVR_TIME struStartTime;//文件的开始时间 + public NET_DVR_TIME struStopTime;//文件的结束时间 + public int dwFileSize;//文件的大小 + public byte[] sCardNum = new byte[32]; + public byte byLocked;//9000设备支持,1表示此文件已经被锁定,0表示正常的文件 + public byte[] byRes = new byte[3]; + } + +//录象文件参数(带卡号) +public static class NET_DVR_FINDDATA_CARD extends Structure +{ + public byte[] sFileName = new byte[100];//文件名 + public NET_DVR_TIME struStartTime;//文件的开始时间 + public NET_DVR_TIME struStopTime;//文件的结束时间 + public int dwFileSize;//文件的大小 + public byte[] sCardNum = new byte[32]; +} + +public static class NET_DVR_FILECOND_V40 extends Structure +{ + public int lChannel; + public int dwFileType; + public int dwIsLocked; + public int dwUseCardNo;//是否带ATM信息进行查询:0-不带ATM信息,1-按交易卡号查询,2-按交易类型查询,3-按交易金额查询,4-按卡号、交易类型及交易金额的组合查询 5-按课程名称查找,此时卡号表示课程名称 + public byte[] sCardNumber = new byte[CARDNUM_LEN_OUT]; + public NET_DVR_TIME struStartTime = new NET_DVR_TIME(); + public NET_DVR_TIME struStopTime = new NET_DVR_TIME(); + public byte byDrawFrame; //0:不抽帧,1:抽帧 + public byte byFindType; //0:查询普通卷,1:查询存档卷 + public byte byQuickSearch; //0:普通查询,1:快速(日历)查询 + public byte bySpecialFindInfoType ; //专有查询条件类型 0-无效, 1-带ATM查询条件 + public int dwVolumeNum; //存档卷号 + public byte[] byWorkingDeviceGUID = new byte[GUID_LEN]; //工作机GUID,通过获取N+1得到 + public NET_DVR_SPECIAL_FINDINFO_UNION uSpecialFindInfo = new NET_DVR_SPECIAL_FINDINFO_UNION(); //专有查询条件 + public byte byStreamType; //0-同一个时间段只返回一种录像,优先级顺序为:主码流、子码流、三码流,1-子码流,2-三码流,3-主码流,254-双码流搜索(优先返回主码流录像,没有主码流录像时返回子码流录像) + public byte byAudioFile; //音频文件 0-非音频文件,1-音频文件 + public byte[] byRes2 = new byte[30]; //保留 +} + +public static class NET_DVR_SPECIAL_FINDINFO_UNION extends Union +{ + public byte[] byLenth = new byte[8] ; + public NET_DVR_ATMFINDINFO struATMFindInfo = new NET_DVR_ATMFINDINFO(); //ATM查询 +} + +public static class NET_DVR_ATMFINDINFO extends Structure +{ + public byte byTransactionType; //交易类型 0-全部,1-查询, 2-取款, 3-存款, 4-修改密码,5-转账, 6-无卡查询 7-无卡存款, 8-吞钞 9-吞卡 10-自定义 + public byte[] byRes = new byte[3] ; //保留 + public int dwTransationAmount ; //交易金额 ; +} + +public static class NET_DVR_FINDDATA_V40 extends Structure +{ + public byte[] sFileName = new byte[100];//文件名 + public NET_DVR_TIME struStartTime = new NET_DVR_TIME();//文件的开始时间 + public NET_DVR_TIME struStopTime = new NET_DVR_TIME();//文件的结束时间 + public int dwFileSize;//文件的大小 + public byte[] sCardNum = new byte[32]; + public byte byLocked;//9000设备支持,1表示此文件已经被锁定,0表示正常的文件 + public byte byFileType; //文件类型:0-定时录像,1-移动侦测 ,2-报警触发, + //3-报警|移动侦测 4-报警&移动侦测 5-命令触发 6-手动录像,7-震动报警,8-环境报警,9-智能报警,10-PIR报警,11-无线报警,12-呼救报警,14-智能交通事件 + public byte byQuickSearch; //0:普通查询结果,1:快速(日历)查询结果 + public byte byRes; + public int dwFileIndex; //文件索引号 + public byte byStreamType; + public byte[] byRes1 = new byte[127]; +} + +public static class NET_DVR_TIME_SEARCH extends Structure +{ + public short wYear; //年,设备OSD时间 + public byte byMonth; //月,设备OSD时间 + public byte byDay; //日,设备OSD时间 + public byte byHour; //时,设备OSD时间 + public byte byMinute; //分,设备OSD时间 + public byte bySecond; //秒,设备OSD时间 + public byte cTimeDifferenceH; //与国际标准时间的时差(小时),-12 ... +14 + public byte cTimeDifferenceM; //与国际标准时间的时差(分钟),-30, 0, 30, 45 + public byte byLocalOrUTC; //0-时差无效,设备本地时间,即设备OSD时间 1-时差有效 + public short wMillisecond; //毫秒,精度不够,默认为0 +} + +public static class NET_DVR_ADDRESS extends Structure +{ + public NET_DVR_IPADDR struIP = new NET_DVR_IPADDR(); //IP地址 + public short wPort; //端口号 + public byte[] byRes = new byte[2]; +} + +public static class NET_DVR_FINDDATA_V50 extends Structure +{ + public byte[] sFileName = new byte[100]; + public NET_DVR_TIME_SEARCH struStartTime = new NET_DVR_TIME_SEARCH(); + public NET_DVR_TIME_SEARCH struStopTime = new NET_DVR_TIME_SEARCH(); + public NET_DVR_ADDRESS struAddr = new NET_DVR_ADDRESS(); //片段所在的地址信息,集群回放时用到 + public int dwFileSize; //文件大小 + public byte byLocked; //文件是否被锁定,1-文件已锁定;0-文件未锁定 + public byte byFileType; //文件类型,与V40相同 + public byte byQuickSearch; //0- 普通查询结果,1- 快速(日历)查询结果 + public byte byStreamType; //码流类型:0- 主码流,1- 子码流,2- 码流三 + public int dwFileIndex; //文件索引号 + public byte[] sCardNum = new byte[32]; //卡号 + public byte[] byRes = new byte[256]; +} + + public static class NET_DVR_FILECOND extends Structure //录象文件查找条件结构 + { + public int lChannel;//通道号 + public int dwFileType;//录象文件类型0xff-全部,0-定时录像,1-移动侦测 ,2-报警触发,3-报警|移动侦测 4-报警&移动侦测 5-命令触发 6-手动录像 + public int dwIsLocked;//是否锁定 0-正常文件,1-锁定文件, 0xff表示所有文件 + public int dwUseCardNo;//是否使用卡号 + public byte[] sCardNumber = new byte[32];//卡号 + public NET_DVR_TIME struStartTime;//开始时间 + public NET_DVR_TIME struStopTime;//结束时间 + } + + public static class NET_DVR_PLAYCOND extends Structure //回放或者下载信息结构体 + { + public int dwChannel;//通道号 + public NET_DVR_TIME struStartTime; + public NET_DVR_TIME struStopTime; + public byte byDrawFrame; //0:不抽帧,1:抽帧 + public byte byStreamType ; //码流类型,0-主码流 1-子码流 2-码流三 + public byte[] byStreamID = new byte[STREAM_ID_LEN]; + public byte[] byRes = new byte[30];//保留 + } + + public static class NET_DVR_VOD_PARA extends Structure //回放或者下载信息结构体 + { + public int dwSize; + public NET_DVR_STREAM_INFO struIDInfo; + public NET_DVR_TIME struBeginTime; + public NET_DVR_TIME struEndTime; + public int hWnd; + public byte byDrawFrame; //0:不抽帧,1:抽帧 + public byte byVolumeType; //0-普通录像卷 1-存档卷 + public byte byVolumeNum; //卷号,目前指存档卷号 + public byte byStreamType; //码流类型 0-主码流, 1-子码流,2-码流三 + public int dwFileIndex; //存档卷上的录像文件索引,搜索存档卷录像时返回的值 + public byte byAudioFile; //音频文件0-否,1-是 + public byte byCourseFile; //课程文件0-否,1-是 + public byte byDownload; //是否下载 0-否,1-是 + public byte byOptimalStreamType; //是否按最优码流类型回放 0-否,1-是(对于双码流设备,某一段时间内的录像文件与指定码流类型不同,则返回实际码流类型的录像) + public byte[] byRes2 = new byte[20]; +} + +//图片查找条件 +public static class NET_DVR_FIND_PICTURE_PARAM extends Structure +{ + public int dwSize; // 结构体大小 + public int lChannel; // 通道号 + public byte byFileType; //图片查找类型 + public byte byNeedCard; // 是否需要卡号 + /* + 0-保留,1-澳,2-京,3-渝,4-闽,5-甘,6-粤,7-桂, + 8-贵,9-琼,10-冀,11-豫,12-黑,13-鄂,14-湘, + 15-吉,16-苏,17-赣,18-辽,19-蒙,20-宁,21-青, + 22-鲁,23-晋,24-陕,25-沪,26-川,27-台,28-津, + 29-藏,30-港,31-新,32-云,33-浙,34-皖,0xff-全部 + */ + public byte byProvince; //省份索引值 + public byte byEventType; // 事件类型:0保留,1-交通事件;2-违章取证;3-其他事件 + public byte[] sCardNum = new byte[CARDNUM_LEN_V30]; // 卡号 + public NET_DVR_TIME struStartTime = new NET_DVR_TIME();//查找图片的开始时间 + public NET_DVR_TIME struStopTime = new NET_DVR_TIME();// 查找图片的结束时间 + //ITC3.7 新增 + public int dwTrafficType; //图片检索生效项 参考 VCA_OPERATE _TYPE + public int dwVehicleType; //车辆类型 参考 VCA_VEHICLE_TYPE + //违规检测类型参考 VCA_ILLEGAL_TYPE 当前不支持复选 + public int dwIllegalType; + public byte byLaneNo; //车道号(1~99) + public byte bySubHvtType ;//0-保留,1-机动车(机动车子类型中支持车牌检索,省份检索),2-非机动车,3-行人 + public byte[] byRes2 = new byte[2]; + public byte[] sLicense = new byte[MAX_LICENSE_LEN/*16*/]; //车牌号码 + public byte byRegion; // 区域索引值 0-保留,1-欧洲(Europe Region),2-俄语区域(Russian Region),3-欧洲&俄罗斯(EU&CIS), 4-中东(Middle East),0xff-所有 + public byte byCountry; // 国家索引值,参照:COUNTRY_INDEX + public byte byArea; //地区 + public byte byISO8601; //是否是8601的时间格式,即时差字段是否有效0-时差无效,年月日时分秒为设备本地时间 1-时差有效 + public byte cStartTimeDifferenceH; //开始时间与UTC的时差(小时),-12 ... +14, 正数表示东时区 + public byte cStartTimeDifferenceM; //开始时间与UTC的时差(分钟),-30, 0, 30, 45,正数表示东时区 + public byte cStopTimeDifferenceH; //结束时间与UTC的时差(小时),-12 ... +14,正数表示东时区 + public byte cStopTimeDifferenceM; //结束时间与UTC的时差(分钟),-30, 0, 30, 45,正数表示东时区 +} + +//查找结果结构体 +public static class NET_DVR_FIND_PICTURE_V40 extends Structure +{ + public byte[] sFileName = new byte[PICTURE_NAME_LEN];//图片名 + public NET_DVR_TIME struTime = new NET_DVR_TIME();//图片的时间 + public int dwFileSize;//图片的大小 + public byte[] sCardNum = new byte[CARDNUM_LEN_V30]; //卡号 + public byte byPlateColor ;//参考结构 VCA_PLATE_COLOR + public byte byVehicleLogo;//参考结构 VLR_VEHICLE_CLASS + public byte byFileType ; //文件类型, :0定时抓图1 移动侦测抓图 2 报警抓图3 报警 | 移动侦测抓图 4 报警 & 移动侦测抓图 6 手动抓图 ,9-智能图片,10- PIR报警,11- 无线报警,12- 呼救报警, 0xa 预览时截图,0xd 人脸侦测, 0xe 越界侦测,0xf 入侵区域侦测,0x10 场景变更侦测, 0x11-设备本地回放时截图, 0x12-智能侦测, 0x32-防区报警, 0x33-紧急求助, 0x34-业务咨询 + public byte byRecogResult ;//识别结果参考结构VTR_RESULT + public byte[] sLicense = new byte[MAX_LICENSE_LEN/*16*/]; //车牌号码 + public byte byEventSearchStatus; //连续图片表示同一查找结果的时候,0-表示后面没有图片信息,1-表示后面还有图片信息。总共图片信息包括最后一张状态为0的图片。 + public byte[] byRes = new byte[75]; // 保留字节 + public NET_DVR_PIC_EXTRA_INFO_UNION uPicExtraInfo; //图片附件信息 +} + +public static class NET_DVR_FACE_EXTRA_INFO extends Union +{ + public NET_VCA_RECT[] struVcaRect = new NET_VCA_RECT[MAX_FACE_PIC_NUM]; //人脸子图坐标信息 + public byte[] byRes = new byte[64]; +} + +//图片附件信息联合体 +public static class NET_DVR_PIC_EXTRA_INFO_UNION extends Union +{ + public byte[] byUnionLen = new byte[544]; //联合体长度,无实际意义 + public NET_DVR_FACE_EXTRA_INFO struFaceExtraInfo; //人脸侦测信息 +} + +//云台区域选择放大缩小(HIK 快球专用) +public static class NET_DVR_POINT_FRAME extends Structure +{ + public int xTop; //方框起始点的x坐标 + public int yTop; //方框结束点的y坐标 + public int xBottom; //方框结束点的x坐标 + public int yBottom; //方框结束点的y坐标 + public int bCounter; //保留 +} + +//语音对讲参数 +public static class NET_DVR_COMPRESSION_AUDIO extends Structure +{ + public byte byAudioEncType; //音频编码类型 0-G722; 1-G711 + public byte byAudioSamplingRate; //音频采样率:0- 默认,1- 16kHZ,2- 32kHZ,3- 48kHZ,4- 44.1kHZ,5- 8kHZ + public byte byAudioBitRate;//音频码率 + public byte[] byres= new byte[4];//保留,置为0 + public byte bySupport;//Mp2l2前4个字节的含义表示后面内容音频数据长度 +} + +public static class NET_DVR_AUDIODEC_INFO extends Structure +{ + public int nchans; /* 声道数 */ + public int sample_rate; /* 采样率 */ + public int aacdec_profile; /* 编码用的框架 */ + public int[] reserved = new int[16]; /* 保留 */ +} + +//音频解码 +public static class NET_DVR_AUDIODEC_PROCESS_PARAM extends Structure +{ + public Pointer in_buf; /* 输入数据buf */ + public Pointer out_buf; /* 输出数据buf */ + public int in_data_size; /* 输入in_buf内数据byte数 */ + public int proc_data_size; /* 输出解码库处理in_buf中数据大小bytes */ + public int out_frame_size; /* 解码一帧后数据BYTE数 */ + public NET_DVR_AUDIODEC_INFO dec_info = new NET_DVR_AUDIODEC_INFO(); /* 输出解码信息 */ + public int g726dec_reset; /* 重置开关 */ + public int g711_type; /* g711编码类型,0 - U law, 1- A law */ + public int[] reserved = new int[16]; /* 保留 */ +} + +public static class NET_DVR_AUDIOENC_INFO extends Structure +{ + public int in_frame_size; /* 输入一帧数据大小(BYTES),由GetInfoParam函数返回 */ + public int[] reserved = new int[16]; /* 保留 */ +} + +//音频编码 +public static class NET_DVR_AUDIOENC_PROCESS_PARAM extends Structure +{ + public Pointer in_buf; /* 输入buf */ + public Pointer out_buf; /* 输出buf */ + public int out_frame_size; /* 编码一帧后的BYTE数 */ + public int g726enc_reset; /* 重置开关 */ + public int g711_type; /* g711编码类型,0 - U law, 1- A law */ + public int enc_mode; /* 音频编码模式,AMR编码配置 */ + public int[] reserved = new int[16]; /* 保留 */ +} + +//用于接收报警信息的缓存区 +public static class RECV_ALARM extends Structure{ + public byte[] RecvBuffer = new byte[4000];//此处的400应不小于最大报警报文长度 +} + +//布防参数 +public static class NET_DVR_SETUPALARM_PARAM extends Structure +{ + public int dwSize; + public byte byLevel; //布防优先级,0-一等级(高),1-二等级(中),2-三等级(低) + public byte byAlarmInfoType; //上传报警信息类型(抓拍机支持),0-老报警信息(NET_DVR_PLATE_RESULT),1-新报警信息(NET_ITS_PLATE_RESULT)2012-9-28 + public byte byRetAlarmTypeV40; //0--返回NET_DVR_ALARMINFO_V30或NET_DVR_ALARMINFO, 1--设备支持NET_DVR_ALARMINFO_V40则返回NET_DVR_ALARMINFO_V40,不支持则返回NET_DVR_ALARMINFO_V30或NET_DVR_ALARMINFO + public byte byRetDevInfoVersion; //CVR上传报警信息回调结构体版本号 0-COMM_ALARM_DEVICE, 1-COMM_ALARM_DEVICE_V40 + public byte byRetVQDAlarmType; //VQD报警上传类型,0-上传报报警NET_DVR_VQD_DIAGNOSE_INFO,1-上传报警NET_DVR_VQD_ALARM + public byte byFaceAlarmDetection; + public byte bySupport; + public byte byBrokenNetHttp; + public short wTaskNo; //任务处理号 和 (上传数据NET_DVR_VEHICLE_RECOG_RESULT中的字段dwTaskNo对应 同时 下发任务结构 NET_DVR_VEHICLE_RECOG_COND中的字段dwTaskNo对应) + public byte byDeployType; //布防类型:0-客户端布防,1-实时布防 + public byte[] byRes1 = new byte[3]; + public byte byAlarmTypeURL;//bit0-表示人脸抓拍报警上传(INTER_FACESNAP_RESULT);0-表示二进制传输,1-表示URL传输(设备支持的情况下,设备支持能力根据具体报警能力集判断,同时设备需要支持URL的相关服务,当前是”云存储“) + public byte byCustomCtrl;//Bit0- 表示支持副驾驶人脸子图上传: 0-不上传,1-上传,(注:只在公司内部8600/8200等平台开放) + + +} + + +//区域框参数 +public static class NET_VCA_RECT extends Structure +{ + public float fX; + public float fY; + public float fWidth; + public float fHeight; +} + +//报警目标信息 +public static class NET_VCA_TARGET_INFO extends Structure +{ + public int dwID; + public NET_VCA_RECT struRect; + public byte[] byRes= new byte[4]; +} + +//前端设备信息 +public static class NET_VCA_DEV_INFO extends Structure +{ + public NET_DVR_IPADDR struDevIP; + public short wPort; + public byte byChannel; + public byte byIvmsChannel; +} + + //视频参数配置 + public static class NET_DVR_VIDEOEFFECT extends Structure + { + public byte byBrightnessLevel; /*0-100*/ + public byte byContrastLevel; /*0-100*/ + public byte bySharpnessLevel; /*0-100*/ + public byte bySaturationLevel; /*0-100*/ + public byte byHueLevel; /*0-100,(保留)*/ + public byte byEnableFunc; //使能,按位表示,bit0-SMART IR(防过曝),bit1-低照度,bit2-强光抑制使能,0-否,1-是 + public byte byLightInhibitLevel; //强光抑制等级,[1-3]表示等级 + public byte byGrayLevel; //灰度值域,0-[0-255],1-[16-235] + } + + //增益配置 + public static class NET_DVR_GAIN extends Structure + { + public byte byGainLevel; /*增益:0-100*/ + public byte byGainUserSet; /*用户自定义增益;0-100,对于抓拍机,是CCD模式下的抓拍增益*/ + public byte[] byRes= new byte[2]; + public int dwMaxGainValue;/*最大增益值,单位dB*/ + } + + //白平衡配置 + public static class NET_DVR_WHITEBALANCE extends Structure + { + public byte byWhiteBalanceMode; /*0-手动白平衡(MWB),1-自动白平衡1(AWB1),2-自动白平衡2 (AWB2),3-自动控制改名为锁定白平衡(Locked WB), + 4-室外(Indoor),5-室内(Outdoor)6-日光灯(Fluorescent Lamp),7-钠灯(Sodium Lamp), + 8-自动跟踪(Auto-Track)9-一次白平衡(One Push),10-室外自动(Auto-Outdoor), + 11-钠灯自动 (Auto-Sodiumlight),12-水银灯(Mercury Lamp),13-自动白平衡(Auto), +14-白炽灯 (IncandescentLamp),15-暖光灯(Warm Light Lamp),16-自然光(Natural Light) */ + public byte byWhiteBalanceModeRGain; /*手动白平衡时有效,手动白平衡 R增益*/ + public byte byWhiteBalanceModeBGain; /*手动白平衡时有效,手动白平衡 B增益*/ + public byte[] byRes= new byte[5]; + } + + //曝光控制 + public static class NET_DVR_EXPOSURE extends Structure + { + public byte byExposureMode; /*0 手动曝光 1自动曝光*/ + public byte byAutoApertureLevel; /* 自动光圈灵敏度, 0-10 */ + public byte[] byRes= new byte[2]; + public int dwVideoExposureSet; /* 自定义视频曝光时间(单位us)*//*注:自动曝光时该值为曝光最慢值 新增20-1s(1000000us)*/ + public int dwExposureUserSet; /* 自定义曝光时间,在抓拍机上应用时,CCD模式时是抓拍快门速度*/ + public int dwRes; + } + + //宽动态配置 + public static class NET_DVR_WDR extends Structure + { + public byte byWDREnabled; /*宽动态:0 dsibale 1 enable 2 auto*/ + public byte byWDRLevel1; /*0-F*/ + public byte byWDRLevel2; /*0-F*/ + public byte byWDRContrastLevel; /*0-100*/ + public byte[] byRes= new byte[16]; + } + + public static class NET_DVR_WDR_CFG extends Structure + { + public int dwSize; //结构体大小 + public NET_DVR_WDR struWDR; //配置信息 + } + + + //日夜转换功能配置 + public static class NET_DVR_DAYNIGHT extends Structure + { + public byte byDayNightFilterType; /*日夜切换:0-白天,1-夜晚,2-自动,3-定时,4-报警输入触发, 5-自动模式2(无光敏),6-黑光,7-黑光自动,8-黑光定时*/ + public byte bySwitchScheduleEnabled; /*0 dsibale 1 enable,(保留)*/ + //定时模式参数 + public byte byBeginTime; /*开始时间(小时),0-23*/ + public byte byEndTime; /*结束时间(小时),0-23*/ + //模式2 + public byte byDayToNightFilterLevel; //0-7 + public byte byNightToDayFilterLevel; //0-7 + public byte byDayNightFilterTime;//(60秒) + //定时模式参数 + public byte byBeginTimeMin; //开始时间(分),0-59 + public byte byBeginTimeSec; //开始时间(秒),0-59 + public byte byEndTimeMin; //结束时间(分),0-59 + public byte byEndTimeSec; //结束时间(秒),0-59 + //报警输入触发模式参数 + public byte byAlarmTrigState; //报警输入触发状态,0-白天,1-夜晚 + } + //Gamma校正 + public static class NET_DVR_GAMMACORRECT extends Structure + { + public byte byGammaCorrectionEnabled; /*0 dsibale 1 enable*/ + public byte byGammaCorrectionLevel; /*0-100*/ + public byte[] byRes= new byte[6]; + } + + //背光补偿配置 + public static class NET_DVR_BACKLIGHT extends Structure + { + public byte byBacklightMode; /*背光补偿:0 off 1 UP、2 DOWN、3 LEFT、4 RIGHT、5MIDDLE、6自定义,10-开,11-自动,12-多区域背光补偿*/ + public byte byBacklightLevel; /*0x0-0xF*/ + public byte[] byRes1 = new byte[2]; + public int dwPositionX1; //(X坐标1) + public int dwPositionY1; //(Y坐标1) + public int dwPositionX2; //(X坐标2) + public int dwPositionY2; //(Y坐标2) + public byte[] byRes= new byte[4]; + } + + //数字降噪功能 + public static class NET_DVR_NOISEREMOVE extends Structure + { + public byte byDigitalNoiseRemoveEnable; /*0-不启用,1-普通模式数字降噪,2-专家模式数字降噪*/ + public byte byDigitalNoiseRemoveLevel; /*普通模式数字降噪级别:0x0-0xF*/ + public byte bySpectralLevel; /*专家模式下空域强度:0-100*/ + public byte byTemporalLevel; /*专家模式下时域强度:0-100*/ + public byte byDigitalNoiseRemove2DEnable; /* 抓拍帧2D降噪,0-不启用,1-启用 */ + public byte byDigitalNoiseRemove2DLevel; /* 抓拍帧2D降噪级别,0-100 */ + public byte[] byRes= new byte[2]; + } + + //CMOS模式下前端镜头配置 + public static class NET_DVR_CMOSMODECFG extends Structure + { + public byte byCaptureMod; //抓拍模式:0-抓拍模式1;1-抓拍模式2 + public byte byBrightnessGate;//亮度阈值 + public byte byCaptureGain1; //抓拍增益1,0-100 + public byte byCaptureGain2; //抓拍增益2,0-100 + public int dwCaptureShutterSpeed1;//抓拍快门速度1 + public int dwCaptureShutterSpeed2;//抓拍快门速度2 + public byte[] byRes= new byte[4]; + } + + //透雾 + public static class NET_DVR_DEFOGCFG extends Structure + { + public byte byMode; //模式,0-不启用,1-自动模式,2-常开模式 + public byte byLevel; //等级,0-100 + public byte[] byRes= new byte[6]; + } + + //电子防抖 + public static class NET_DVR_ELECTRONICSTABILIZATION extends Structure + { + public byte byEnable;//使能 0- 不启用,1- 启用 + public byte byLevel; //等级,0-100 + public byte[] byRes= new byte[6]; + } + + //走廊模式 + public static class NET_DVR_CORRIDOR_MODE_CCD extends Structure + { + public byte byEnableCorridorMode; //是否启用走廊模式 0~不启用, 1~启用 + public byte[] byRes= new byte[11]; + } + + // SMART IR(防过曝)配置参数 + public static class NET_DVR_SMARTIR_PARAM extends Structure + { + public byte byMode;//0~手动,1~自动 + public byte byIRDistance;//红外距离等级(等级,距离正比例)level:1~100 默认:50(手动模式下增加) + public byte byShortIRDistance;// 近光灯距离等级(1~100) + public byte byLongIRDistance;// 远光灯距离等级(1~100) + } + + //在byIrisMode 为P-Iris1时生效,配置红外光圈大小等级,配置模式 + public static class NET_DVR_PIRIS_PARAM extends Structure + { + public byte byMode;//0-自动,1-手动 + public byte byPIrisAperture;//红外光圈大小等级(等级,光圈大小正比例)level:1~100 默认:50(手动模式下增加) + public byte[] byRes= new byte[6]; + } + + + //激光参数配置 2014-02-25 + public static class NET_DVR_LASER_PARAM_CFG extends Structure + { + //Length = 16 + public byte byControlMode; //控制模式 0-无效,1-自动,2-手动 默认自动 + public byte bySensitivity; //激光灯灵敏度 0-100 默认50 + public byte byTriggerMode; //激光灯触发模式 0-无效,1-机芯触发,2-光敏触发 默认机芯触发 + public byte byBrightness; //控制模式为手动模式下有效;激光灯亮度 0-255 默认100 + public byte byAngle; //激光灯角度 0-无效,范围1-36 默认12,激光灯照射范围为一个圆圈,调节激光角度是调节这个圆的半径的大小 + public byte byLimitBrightness; //控制模式为自动模式下有效;激光灯亮度限制 0~100 (新增)2014-01-26 + public byte byEnabled ; //手动控制激光灯使能 0-关闭,1-启动 + public byte byIllumination; //激光灯强度配置0~100 + public byte byLightAngle; //补光角度 0~100 + public byte[] byRes= new byte[7]; //保留 + } + + public static class NET_DVR_FFC_PARAM extends Structure + { + //1-Schedule Mode,2-Temperature Mode, 3-Off + public byte byMode; + //(时间:按能力显示,单位分钟,选项有10,20,30,40,50,60,120,180,240) + public byte byRes1; + public short wCompensateTime; //定时模式下生效 + public byte[] byRes= new byte[4]; + } + + public static class NET_DVR_DDE_PARAM extends Structure //在sensor中完成 + { + public byte byMode;//1-Off,2-Normal Mode,3-Expert Mode + public byte byNormalLevel;//普通模式等级范围[1,100],普通模式下生效 + public byte byExpertLevel;//专家模式等级范围[1,100],专家模式下生效 + public byte[] byRes= new byte[5]; + } + + public static class NET_DVR_AGC_PARAM extends Structure + { + public byte bySceneType;//1-Normal Sence,2-Highlight Sence,3-Manual Sence + public byte byLightLevel;//亮度等级[1,100];手动模式下生效 + public byte byGainLevel; //增益等级[1,100];手动模式下生效 + public byte[] byRes= new byte[5]; + } + + //抓拍机CCD参数 共64字节 + public static class NET_DVR_SNAP_CAMERAPARAMCFG extends Structure + { + public byte byWDRMode; // 宽动态模式;0~关闭,1~数字宽动态 2~宽动态 + public byte byWDRType; // 宽动态切换模式; 0~强制启用,1~按时间启用,2~按亮度启用 + public byte byWDRLevel; // 宽动态等级,0~6索引对应1-7,默认索引2(即3级); + public byte byRes1; + NET_DVR_TIME_EX struStartTime; //开始宽动态时间 + NET_DVR_TIME_EX struEndTime; //结束宽动态时间 + public byte byDayNightBrightness; //日夜转换亮度阈值,0-100,默认50; + //记忆色增强 + public byte byMCEEnabled;//记忆色增强使能,true:开启,false:关闭 + public byte byMCELevel;//记忆色增强强度,0~100,默认值50 + //自动对比度 + public byte byAutoContrastEnabled;//自动对比度使能,true:开启,false:关闭 + public byte byAutoContrastLevel;//自动对比等级(0-100),默认50 + //细节增强 + public byte byLSEDetailEnabled;//细节增强使能,true:开启,false:关闭 + public byte byLSEDetailLevel;//细节增强等级(0-100),默认50 + // License Plate Definition Enhancement车牌增强 + public byte byLPDEEnabled;//车牌增强使能,true:开启,false:关闭 + public byte byLPDELevel;//车牌增强等级(0-100),默认50 + //对比度增强 + public byte byLseEnabled; //对比度增强使能,true:开启,false:关闭 + public byte byLseLevel; //对比度增强等级(0-100),默认0 + public byte byLSEHaloLevel;//光晕抑制等级。范围 0-100,默认0 + public byte byLseType; //对比度增强切换模式; 0~强制启用,1~按时间启用,2~按亮度启用(该字段可同时控制byLseLevel、byLSEHaloLevel两个参数) + public byte[] byRes2= new byte[3]; + public NET_DVR_TIME_EX struLSEStartTime; //开始对比度增强时间(当byLseType为1时生效) + public NET_DVR_TIME_EX struLSEEndTime; //结束对比度增强时间(当byLseType为1时生效) + public byte byLightLevel;//为亮度等级参数(0-100),默认0,(当byLseType为2时生效) + //车牌对比度 + public byte byPlateContrastLevel;//车牌对比度等级,0~100,默认0 + //车牌饱和度 + public byte byPlateSaturationLevel;//车牌饱和度等级,0~100,默认0 + public byte[] byRes= new byte[9]; + } + + //光学透雾参数 + public static class NET_DVR_OPTICAL_DEHAZE extends Structure + { + public byte byEnable; //0~不启用光学透雾,1~启用光学透雾 + public byte[] byRes= new byte[7]; + } + + //测温AGC设置,当测温AGC模式为无效时,以NET_DVR_AGC_PARAM配置参数为准,当测温AGC模式为自动或者手动时,NET_DVR_AGC_PARAM配置参数无效 + public static class NET_DVR_THERMOMETRY_AGC extends Structure + { + public byte byMode;//AGC模式,0~无效,1~自动,2~手动 + public byte[] byRes1= new byte[3]; + int iHighTemperature;//最高温度,范围为:-273~9999摄氏度(1~手动模式下生效) + int iLowTemperature;//最低温度,范围为:-273~9999摄氏度(1~手动模式下生效) + public byte[] byRes= new byte[8]; + } + +public static class NET_DVR_CAMERAPARAMCFG_EX extends Structure +{ + public int dwSize; + public NET_DVR_VIDEOEFFECT struVideoEffect;/*亮度、对比度、饱和度、锐度、色调配置*/ + public NET_DVR_GAIN struGain;/*自动增益*/ + public NET_DVR_WHITEBALANCE struWhiteBalance;/*白平衡*/ + public NET_DVR_EXPOSURE struExposure; /*曝光控制*/ + public NET_DVR_GAMMACORRECT struGammaCorrect;/*Gamma校正*/ + public NET_DVR_WDR struWdr;/*宽动态*/ + public NET_DVR_DAYNIGHT struDayNight;/*日夜转换*/ + public NET_DVR_BACKLIGHT struBackLight;/*背光补偿*/ + public NET_DVR_NOISEREMOVE struNoiseRemove;/*数字降噪*/ + public byte byPowerLineFrequencyMode; /*0-50HZ; 1-60HZ*/ + /* + 0-自动光圈, + 1-手动光圈, + 2-P-Iris1, + 3-Union 3-9mm F1.6-2.7 (T5280-PQ1) [IPC5.1.7] + 4-Union 2.8-12mm F1.6-2.7 (T5289-PQ1) [IPC5.1.7] + 5-HIK 3.8-16mm F1.5(HV3816P-8MPIR) + 6-HIK 11-40mm F1.7 (HV1140P-8MPIR) + 7-HIK 2.7-12mm F1.2(TV2712P-MPIR) + 8- MZ5721D-12MPIR + 9- MZ1555D-12MPIR + 10- MZ5721D-12MPIR(RS485) + 11- MZ1555D-12MPIR(RS485) + */ + public byte byIrisMode; + public byte byMirror ; /* 镜像:0 off,1- leftright,2- updown,3-center 4-Auto*/ + public byte byDigitalZoom; /*数字缩放:0 dsibale 1 enable*/ + public byte byDeadPixelDetect; /*坏点检测,0 dsibale 1 enable*/ + public byte byBlackPwl;/*黑电平补偿 , 0-255*/ + public byte byEptzGate;// EPTZ开关变量:0-不启用电子云台,1-启用电子云台 + public byte byLocalOutputGate;//本地输出开关变量0-本地输出关闭1-本地BNC输出打开 2-HDMI输出关闭 + //20-HDMI_720P50输出开 + //21-HDMI_720P60输出开 + //22-HDMI_1080I60输出开 + //23-HDMI_1080I50输出开 + //24-HDMI_1080P24输出开 + //25-HDMI_1080P25输出开 + //26-HDMI_1080P30输出开 + //27-HDMI_1080P50输出开 + //28-HDMI_1080P60输出开 + public byte byCoderOutputMode;//编码器fpga输出模式0直通3像素搬家 + public byte byLineCoding; //是否开启行编码:0-否,1-是 + public byte byDimmerMode; //调光模式:0-半自动,1-自动 + public byte byPaletteMode; //调色板:0-白热,1-黑热,2-调色板2,…,8-调色板8, 9-融合1,10-彩虹,11-融合2,12-铁红1,13-铁红2,14-深褐色,15-色彩1,16-色彩2,17-冰火,18-雨,19-红热,20-绿热,21-深蓝,22-色彩3 + public byte byEnhancedMode; //增强方式(探测物体周边):0-不增强,1-1,2-2,3-3,4-4 + public byte byDynamicContrastEN; //动态对比度增强 0-1 + public byte byDynamicContrast; //动态对比度 0-100 + public byte byJPEGQuality; //JPEG图像质量 0-100 + public NET_DVR_CMOSMODECFG struCmosModeCfg;//CMOS模式下前端参数配置,镜头模式从能力集获取 + public byte byFilterSwitch; //滤波开关:0-不启用,1-启用 + public byte byFocusSpeed; //镜头调焦速度:0-10 + public byte byAutoCompensationInterval; //定时自动快门补偿:1-120,单位:分钟 + public byte bySceneMode; //场景模式:0-室外,1-室内,2-默认,3-弱光 + public NET_DVR_DEFOGCFG struDefogCfg;//透雾参数 + public NET_DVR_ELECTRONICSTABILIZATION struElectronicStabilization;//电子防抖 + public NET_DVR_CORRIDOR_MODE_CCD struCorridorMode;//走廊模式 + public byte byExposureSegmentEnable; //0~不启用,1~启用 曝光时间和增益呈阶梯状调整,比如曝光往上调整时,先提高曝光时间到中间值,然后提高增益到中间值,再提高曝光到最大值,最后提高增益到最大值 + public byte byBrightCompensate;//亮度增强 [0~100] + /* + 0-关闭、1-640*480@25fps、2-640*480@30ps、3-704*576@25fps、4-704*480@30fps、5-1280*720@25fps、6-1280*720@30fps、 + 7-1280*720@50fps、8-1280*720@60fps、9-1280*960@15fps、10-1280*960@25fps、11-1280*960@30fps、 + 12-1280*1024@25fps、13--1280*1024@30fps、14-1600*900@15fps、15-1600*1200@15fps、16-1920*1080@15fps、 + 17-1920*1080@25fps、18-1920*1080@30fps、19-1920*1080@50fps、20-1920*1080@60fps、21-2048*1536@15fps、22-2048*1536@20fps、 + 23-2048*1536@24fps、24-2048*1536@25fps、25-2048*1536@30fps、26-2560*2048@25fps、27-2560*2048@30fps、 + 28-2560*1920@7.5fps、29-3072*2048@25fps、30-3072*2048@30fps、31-2048*1536@12.5、32-2560*1920@6.25、 + 33-1600*1200@25、34-1600*1200@30、35-1600*1200@12.5、36-1600*900@12.5、37-1280*960@12.5fps、38-800*600@25fps、39-800*600@30fps40、 + 4000*3000@12.5fps、41-4000*3000@15fps、42-4096*2160@20fps、43-3840*2160@20fps 、44-960*576@25fps、45-960*480@30fps、46-752*582@25fps、 + 47-768*494@30fps、48-2560*1440@25fps、49-2560*1440@30fps 、50-720P@100fps、51-720P@120fps、52-2048*1536@50fps、53-2048*1536@60fps、 + 54-3840*2160@25fps、55-3840*2160@30fps、56-4096*2160@25fps、57-4096*2160@30fps 、58-1280*1024@50fps、59-1280*1024@60fps、 + 60-3072*2048@50fps、61-3072*2048@60fps、62-3072*1728@25fps、63-3072*1728@30fps、64-3072*1728@50fps、65-3072*1728@60fps、66-336*256@50fps、67-336*256@60fps、 + 68-384*288@50fps、69-384*288@60fps 、70- 640 * 512@50fps 、71- 640 * 512@60fps、72-2592*1944@25fps、73-2592*1944@30fps、74-2688*1536@25fps、75-2688*1536@30fps + 76-2592*1944@20fps、77-2592*1944@15fps、78-2688*1520@20fps、79-2688*1520@15fps、80-2688*1520@25fps、81-2688*1520@30fps、82- 2720*2048@25fps、 83- 2720*2048@30fps、 + 84-336*256@25fps、85- 384*288@25fps、86-640*512@25fps、87-1280*960@50fps、88-1280*960@60fps、89-1280*960@100fps、90-1280*960@120fps、91-4000*3000@20fps、 + 92-1920*1200@25fps、93-1920*1200@30fps、94-2560*1920@25fps、95-2560*1920@20fps、96-2560*1920@30fps、97-1280*1920@25fps、98-1280*1920@30fps + 99-4000*3000@24fps、100-4000*3000@25fps、101-4000*3000@10fps、102- 384*288@30fps、103-2560*1920@15fps、104-2400*3840@25fps、105-1200*1920@25fps + 106-4096*1800@30fps、107-3840*1680@30fps、108-2560*1120@30fps、109-704*320@30fps、110-1280*560@30fps、111-4096*1800@25fps、112-3840*1680@25fps + 113-2560*1120@25fps、114-704*320@25fps、115-1280*560@25fps、116-2400*3840@24fps、117-3840*2400@24fps、118-3840*2400@25fps、119-2560*1920@12.5fps + 120-2560*2048@12fps、121-2560*2048@15fps、122-2560*1536@25fps、123-2560*1536@30fps、124-2256*2048@25fps、125-2256*2048@30fps、126-2592*2592@12.5fps、127-2592*2592@15fps、 + 128 - 640*512@30fps、129-2048*1520@30fps、130-2048*1520@25fps、131-3840*2160@24fps、132-2592*1520@25fps、133-2592*1520@30fps、134-2592*1536@25fps、135-2592*1536@30fps + 136-640*960@25fps、137-640*960@24fps、142-2992*2192@25fps、143-2992*2192@30fps、144-3008*2160@25fps、145-3008*2160@30fps、146-3072*1728@20fps、147-2560*1440@20fps、 + 148-2160*3840@25fps、149-2160*3840@30fps、150-7008*1080@25fps、151-7008*1080@30fps、152-3072*2048@20fps、153-1536*864@25fps、154-2560*1920@24fps、155-2400*3840@30fps、 + 156-3840*2400@30fps、157-3840*2160@15fps + 158-384*288@8.3fps、159-640*512@8.3fps、160-160*120@8.3fps、161-1024*768@8.3fps、162-640*480@8.3fps、163-3840*2160@12.5fps、164-2304*1296@30fps、165-2304*1296@25fps、 + 166-2560*1440@24fps、167-2688*1512@25fps、168-2688*1512@30fps、169-2688*1512@50fps、170-2688*1512@60fps、171-1536*864@30fps、172-2560*1440@50fps、173-2560*1440@60fps、 + 174-2048*2048@25fps、175-2048*2048@30fps、176-4000*3060@20fps、177-3060*3060@25fps、178-3060*3060@30fps、179-3000*3000@25fps、180-3000*3000@30fps、181-8160*3616@30fps、 + 182-8160*3616@25fps、183-3000*3000@20fps、184-3000*3000@15fps、185-3000*3000@12.5fps、186-5472*3648@25fps、187-5472*3648@30fps、188-7680*4320@25fps、189-7680*4320@30fps、 + 190-8160*2400@25fps、191-8160*2400@30fps、192-5520*2400@25fps、193-5520*2400@30fps、194-2560*1440@15fps、195-1944*1212@24fps、196-1944*1212@25fps、197-3456*1920@30fps、 + 198-4800*2688@25fps、199-4800*2688@30fps、200-6480*1080@25fps、201-6480*1080@30fps、202-8640*1440@25fps、203-8640*1440@30fps、204-3456*1920@25fps、205-2688*1520@50fps、 + 206-2688*1520@60fps、207-4976*1452@25fps、208-4976*1452@30fps、 209-3200*1800@25fps、210-3200*1800@30fps、211-5472*3648@24fps、212-1920*1080@12.5fps、213-2944*1656@20fps、 + 214-1920*1080@24fps、215-4800*1600@25fps、216-4800*1600@30fps、217-2560*1440@12.5fps、218-6560*3690@1fps、219-5120*1400@20fps、220-7680*4320@1fps、221-1920*1080@20fps + 222-5120*1440@20fps、223-4080*1808@25fps、224-4080*1808@30fps、225-4080*1152@25fps、226-4080*1152@30fps、227-2688*1944@20fps、228-2592*1944@24fps、229-3200*1800@15fps、 + 230-4416*1696@20fps、231-3456*1080@25fps、232-3200*1800@12.5fps*/ + public byte byCaptureModeN; //视频输入模式(N制) + public byte byCaptureModeP; //视频输入模式(P制) + public NET_DVR_SMARTIR_PARAM struSmartIRParam; //红外放过爆配置信息 + public NET_DVR_PIRIS_PARAM struPIrisParam;//PIris配置信息对应byIrisMode字段从2-PIris1开始生效 + //2014-02-25 新增参数 + public NET_DVR_LASER_PARAM_CFG struLaserParam; //激光参数 + public NET_DVR_FFC_PARAM struFFCParam; + public NET_DVR_DDE_PARAM struDDEParam; + public NET_DVR_AGC_PARAM struAGCParam; + public byte byLensDistortionCorrection;//镜头畸变校正 0-关闭,1-开启 + public byte byDistortionCorrectionLevel;//畸变校正等级:0-保留;1-等级一;2-等级二;3-等级三;255-自定义 + public byte byCalibrationAccurateLevel;// 畸变校正强度[0-100] + public byte byZoomedInDistantViewLevel;//远端放大等级[0-100] + public NET_DVR_SNAP_CAMERAPARAMCFG struSnapCCD ; //抓拍机CCD参数,只用于抓拍机 + public NET_DVR_OPTICAL_DEHAZE struOpticalDehaze;//光学透雾参数 + public NET_DVR_THERMOMETRY_AGC struThermAGC;//测温AGC配置 + public byte byFusionMode;//双光谱视频融合模式,0~热成像模式,1~融合模式,2~画中画模式,3~可见光模式, 4~融合黑白模式, 5~融合彩色模式-草地,6~融合彩色模式-荒地,7~融合彩色模式-雪地,8~融合彩色模式-海洋,9~融合彩色模式-城市 + public byte byHorizontalFOV;//水平视场角[0-100] + public byte byVerticalFOV;//垂直视场角[0-100] + public byte byBrightnessSuddenChangeSuppression;//亮度突变抑制0-关闭,1-开启 + public byte byGPSEnabled;//GPS开关使能,0-关,1-开 + public byte[] byRes= new byte[155]; +} + + +//事件规则信息 +public static class NET_VCA_RULE_INFO extends Structure +{ + public byte byRuleID; + public byte byRes; + public short wEventTypeEx; + public byte[] byRuleName= new byte[NAME_LEN]; + public int dwEventType; + public NET_VCA_EVENT_UNION uEventParam; + public void read() { + super.read(); + switch (wEventTypeEx) { + case 1: + uEventParam.setType(NET_VCA_TRAVERSE_PLANE.class); + break; + case 2: + case 3: + uEventParam.setType(NET_VCA_AREA.class); + break; + default: + break; + } + uEventParam.read(); + } + public void write() { + super.write(); + uEventParam.write(); + } + +} + +//警戒规则参数联合体 +public static class NET_VCA_EVENT_UNION extends Union +{ + public int[] uLen = new int[23]; + public NET_VCA_TRAVERSE_PLANE struTraversePlane; + public NET_VCA_AREA struArea; +} + +//穿越警戒面参数 +public static class NET_VCA_TRAVERSE_PLANE extends Structure +{ + public NET_VCA_LINE struPlaneBottom; + public int dwCrossDirection; + public byte bySensitivity; + public byte byPlaneHeight; + public byte byDetectionTarget;/*检测目标:0- 所有目标,1- 人,2- 车 */ + public byte[] byRes2= new byte[37]; +} + + public static class NET_DVR_HANDLEEXCEPTION_V40 extends Structure + { + public int dwHandleType;/*处理方式,各种异常处理方式的"或"结果,异常处理方式: + 0x00: 无响应 0x01: 监视器上警告 0x02: 声音警告 0x04: 上传中心 + 0x08: 触发报警输出 0x10: Jpeg抓图并上传EMail + 0x20: 无线声光报警器联动 0x40: 联动电子地图(目前仅PCNVR支持) + 0x200:抓图并上传ftp 0x400: 虚焦侦测联动聚焦 + 0x800: PTZ联动跟踪(球机跟踪目标) + E.g. dwHandleType==0x01|0x04 表示配置报警发生时联动监视器上警告并且将报警信息上传中心。 */ + public int dwMaxRelAlarmOutChanNum;/*设备最大支持的触发报警输出通道数(只读) */ + public int dwRelAlarmOutChanNum ;/*已配置的触发的报警输出通道个数,决定dwRelAlarmOut取前多少个数组下标 */ + public int[] dwRelAlarmOut = new int[MAX_CHANNUM_V30];/*触发报警输出通道,取数组前dwRelAlarmOutChanNum个值, + 其值表示报警输出通道号(从1开始),初始值是0xfffffffff(不关联通道)。 + 例如,dwRelAlarmOutChanNum=5,则可以配置触发报警输出通道dwRelAlarmOut[0]~dwRelAlarmOut[4]。 */ + public byte[] byRes = new byte[64]; /*保留,置为0 */ +} + +public static final int MAX_ALERTLINE_NUM = 8; +public static class NET_VCA_TRAVERSE_PLANE_DETECTION extends Structure +{ + public int dwSize; + public byte byEnable;//使能 + public byte byEnableDualVca;// 启用支持智能后检索 0-不启用,1-启用 + public byte[] byRes1 = new byte[2]; + public NET_VCA_TRAVERSE_PLANE[] struAlertParam = new NET_VCA_TRAVERSE_PLANE[MAX_ALERTLINE_NUM]; //警戒线参数 + public NET_DVR_SCHEDTIMEWEEK[] struAlarmSched = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS]; + public NET_DVR_HANDLEEXCEPTION_V40 struHandleException; //异常处理方式 + public int dwMaxRelRecordChanNum ; //报警触发的录象通道 数(只读)最大支持数量 + public int dwRelRecordChanNum ; //报警触发的录象通道 数 实际支持的数量 + public int[] byRelRecordChan = new int[MAX_CHANNUM_V30];//触发录像的通道号 + public NET_DVR_SCHEDTIME[] struHolidayTime = new NET_DVR_SCHEDTIME[MAX_TIMESEGMENT_V30]; //假日布防时间 + public byte[] byRes2 = new byte[100]; +} + +public static class NET_VCA_INTRUSION extends Structure +{ + public NET_VCA_POLYGON struRegion;//区域范围 + public short wDuration; //行为事件触发时间阈值: 1-120秒,建议5秒,判断是有效报警的时间 在ATM系统中触发文件阈值为 1-1000秒 + public byte bySensitivity; //灵敏度参数,范围[1-100] + public byte byRate; //占比:区域内所有未报警目标尺寸目标占区域面积的比重,归一化为-; + /* + 检测目标,可支持多选,具体定义为: + 0~所有目标(表示不锁定检测目标,所有目标都将进行检测) + 0x01 ~ 人, + 0x02 ~ 车, + 0x04 ~ 其他 + */ + public byte byDetectionTarget; + public byte byPriority;//优先级,0~低,1~中,2~高 + public byte[] byRes = new byte[2]; //保留 +} + +public static final int MAX_INTRUSIONREGION_NUM = 8; //最大区域数数 +public static class NET_VCA_FIELDDETECION extends Structure +{ + public int dwSize; + public byte byEnable; //使能,是否开启 + public byte byEnableDualVca;// 启用支持智能后检索 0-不启用,1-启用 + public byte byEnableHumanMisinfoFilter;// 启用人体去误报 0-不启用,1-启用 + public byte byEnableVehicleMisinfoFilter;// 启用车辆去误报 0-不启用,1-启用 + public NET_VCA_INTRUSION[] struIntrusion = new NET_VCA_INTRUSION[MAX_INTRUSIONREGION_NUM];//每个区域的参数设置 + public NET_DVR_SCHEDTIMEWEEK[] struAlarmSched = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS]; //布防时间 + public NET_DVR_HANDLEEXCEPTION_V40 struHandleException; //异常处理方式 + public int dwMaxRelRecordChanNum ; //报警触发的录象通道 数(只读)最大支持数量 + public int dwRelRecordChanNum ; //报警触发的录象通道 数 实际支持的数量 + public int[] byRelRecordChan = new int[MAX_CHANNUM_V30];//触发录像通道 + public NET_DVR_SCHEDTIME[] struHolidayTime= new NET_DVR_SCHEDTIME[MAX_TIMESEGMENT_V30]; //假日布防时间 + public byte[] byRes2 = new byte[100]; +} + +public static class NET_DVR_CHANNEL_GROUP extends Structure +{ + public int dwSize; + public int dwChannel; + public int dwGroup; + public byte byID; + public byte[] byRes1 = new byte[3]; + public int dwPositionNo; + public byte[] byRes = new byte[56]; +} + +//线结构参数 +public static class NET_VCA_LINE extends Structure +{ + public NET_VCA_POINT struStart; + public NET_VCA_POINT struEnd; +} + +//点坐标参数 +public static class NET_VCA_POINT extends Structure +{ + public float fX; + public float fY; + + +} + +//进入/离开区域参数 +public static class NET_VCA_AREA extends Structure +{ + public NET_VCA_POLYGON struRegion; + public byte[] byRes= new byte[8]; +} + +//多边形结构体 +public static class NET_VCA_POLYGON extends Structure +{ + public int dwPointNum; + public NET_VCA_POINT[] struPos= new NET_VCA_POINT[VCA_MAX_POLYGON_POINT_NUM]; +} + +public static class NET_VCA_SIZE_FILTER extends Structure { + public byte byActive; //是否激活尺寸过滤器 0-否 非0-是 + public byte byMode; //过滤器模式SIZE_FILTER_MODE + public byte[] byRes = new byte[2]; //保留,置0 + public NET_VCA_RECT struMiniRect; //最小目标框,全0表示不设置 + public NET_VCA_RECT struMaxRect; //最大目标框,全0表示不设置 +} + + +//尺寸过滤策略 +public static class NET_VCA_FILTER_STRATEGY extends Structure { + public byte byStrategy; //尺寸过滤策略 0 - 不启用 1-高度和宽度过滤,2-面积过滤 + public byte[] byRes = new byte[11]; //保留 +} + +//行为分析报警 +public static class NET_VCA_RULE_ALARM extends Structure +{ + public int dwSize; + public int dwRelativeTime; + public int dwAbsTime; + public NET_VCA_RULE_INFO struRuleInfo; + public NET_VCA_TARGET_INFO struTargetInfo; + public NET_VCA_DEV_INFO struDevInfo; + public int dwPicDataLen; + public byte byPicType; + public byte byRelAlarmPicNum; //关联通道报警图片数量 + public byte bySmart;//IDS设备返回0(默认值),Smart Functiom Return 1 + public byte byPicTransType; //图片数据传输方式: 0-二进制;1-url + public int dwAlarmID; //报警ID,用以标识通道间关联产生的组合报警,0表示无效 + public short wDevInfoIvmsChannelEx; //与NET_VCA_DEV_INFO里的byIvmsChannel含义相同,能表示更大的值。老客户端用byIvmsChannel能继续兼容,但是最大到255。新客户端版本请使用wDevInfoIvmsChannelEx。 + public byte byRelativeTimeFlag; //dwRelativeTime字段是否有效 0-无效, 1-有效,dwRelativeTime表示UTC时间 + public byte byAppendInfoUploadEnabled; //附加信息上传使能 0-不上传 1-上传 + public Pointer pAppendInfo; //指向附加信息NET_VCA_APPEND_INFO的指针,byAppendInfoUploadEnabled为1时或者byTimeDiffFlag为1时有效 + public Pointer pImage; +} + +public static class NET_DVR_SYSTEM_TIME extends Structure { + public short wYear; //年 + public short wMonth; //月 + public short wDay; //日 + public short wHour; //时 + public short wMinute; //分 + public short wSecond; //秒 + public short wMilliSec; //毫秒 + public byte[] byRes = new byte[2]; +} + +//设备支持AI开放平台接入,上传视频检测数据 +public static class NET_AIOP_VIDEO_HEAD extends Structure { + public int dwSize; //dwSize = sizeof(NET_AIOP_VIDEO_HEAD) + public int dwChannel; //设备分析通道的通道号; + public NET_DVR_SYSTEM_TIME struTime = new NET_DVR_SYSTEM_TIME(); //时间 + public byte[] szTaskID = new byte[64]; //视频任务ID,来自于视频任务派发 + public int dwAIOPDataSize; //对应AIOPDdata数据长度 + public int dwPictureSize; //对应分析图片长度 + public byte[] szMPID = new byte[64]; //检测模型包ID,用于匹配AIOP的检测数据解析;可以通过URI(GET /ISAPI/Intelligent/AIOpenPlatform/algorithmModel/management?format=json)获取当前设备加载的模型包的label description信息; + public Pointer pBufferAIOPData; //AIOPDdata数据 + public Pointer pBufferPicture;//对应分析图片数据 + public byte byPictureMode;//图片数据传输模式 0-二进制,1-武汉云云存储,当byPictureMode为0时pBufferPicture为二进制数据,当byPictureMode为1时pBufferPicture为武汉云URL + public byte[] byRes2 = new byte[3];//保留字节 + public int dwPresetIndex; //预置点序号 + public byte[] byRes = new byte[176]; +} + +//设备支持AI开放平台接入,上传图片检测数据 +public static class NET_AIOP_PICTURE_HEAD extends Structure { + public int dwSize; //dwSize = sizeof(NET_AIOP_PICTURE_HEAD) + public NET_DVR_SYSTEM_TIME struTime = new NET_DVR_SYSTEM_TIME(); //时间 + public byte[] szPID = new byte[64]; //透传下发的图片ID,来自于图片任务派发 + public int dwAIOPDataSize; //对应AIOPDdata数据长度 + public byte byStatus; //状态值:0-成功,1-图片大小错误 + public byte[] byRes1 = new byte[3]; + public byte[] szMPID = new byte[64]; //检测模型包ID,用于匹配AIOP的检测数据解析; + public Pointer pBufferAIOPData;//AIOPDdata数据 + public int dwPresetIndex; //预置点序号 + public byte[] byRes = new byte[180]; +} + +//规则触发参数 +public static class NET_VCA_RULE_TRIGGER_PARAM extends Structure { + public byte byTriggerMode; //规则的触发方式,0- 不启用,1- 轨迹点 2- 目标面积 + public byte byTriggerPoint; //触发点,触发方式为轨迹点时有效 0- 中,1-上,2-下 + public byte[] byRes1 = new byte[2]; //保留 + public float fTriggerArea; //触发目标面积百分比 [0,100],触发方式为目标面积时有效 + public byte[] byRes2 = new byte[4]; //保留 +} + +public static class NET_VCA_ONE_RULE_V42 extends Structure { + public byte byActive; //是否激活规则, 0-否,非0-是 + public byte byEventPriority;//事件优先级 0-低,1-中,2-高 + public byte[] byRes1 = new byte[4]; //保留,设置为0字段 + public short wEventType; //行为事件类型,参考VCA_RULE_EVENT_TYPE_EX + public byte[] byRuleName = new byte[NAME_LEN/*32*/]; //规则名称 + public NET_VCA_EVENT_UNION uEventParam; //行为分析事件参数 + public NET_VCA_SIZE_FILTER struSizeFilter; //尺寸过滤器 + public NET_DVR_SCHEDTIMEWEEK[] struAlarmTime = new NET_DVR_SCHEDTIMEWEEK[MAX_DAYS];//布防时间 + public NET_DVR_HANDLEEXCEPTION_V40 struAlarmHandleType; /*处理方式*/ + //异常处理方式中报警输出号与组号绑定,即组号为0时,表示关联的报警输出号范围为1-64,当组号为1时,表示关联的报警输出号范围为65-128, 且是组内紧凑排列,如果遇到0xffffffff表示本组 当前的及组内后续的报警出号无效 + public int[] dwRelRecordChan = new int[MAX_CHANNUM_V30]; /* 报警触发的录象通道(四字节的通道号,初始值是 0xffffffff)*/ + //关联的录像通道号与组号绑定,即组号为0时,表示关联的通道号范围为1-64,当组号为1时,表示关联的通道号范围为65-128, 且是组内紧凑排列,如果遇到0xffffffff表示本组 当前的及组内后续的关联通道号无效 + public short wAlarmDelay; //智能报警延时,0-5s,1-10,2-30s,3-60s,4-120s,5-300s,6-600s + public byte[] byRes2 = new byte[2]; //保留 + public NET_VCA_FILTER_STRATEGY struFilterStrategy; //尺寸过滤策略 + public NET_VCA_RULE_TRIGGER_PARAM struTriggerParam; //规则触发参数 + public byte[] byRes = new byte[32]; +} + +public static class NET_DVR_PTZ_POSITION extends Structure { + // 是否启用场景,在设置场景行为规则的时候该字段无效,在设置球机本地配置场景位置信息时作为使能位 + public byte byEnable; + public byte[] byRes1 = new byte[3]; //保留 + public byte[] byPtzPositionName = new byte[NAME_LEN]; //场景位置名称 + public NET_DVR_PTZPOS struPtzPos; //ptz 坐标 + public byte[] byRes2 = new byte[40]; +} + +//行为分析配置结构体 +public static class NET_VCA_RULECFG_V42 extends Structure { + public int dwSize; //结构图大小 + public byte byPicProType; //报警时图片处理方式 0-不处理 1-上传 + public byte byUpLastAlarm; //是否先上传最近一次的报警,0-否,1-是 + public byte byPicRecordEnable; //是否启用图片存储, 0-不启用, 1-启用 + public byte byRes1; + public NET_DVR_JPEGPARA struPicParam; //图片规格结构 + public NET_VCA_ONE_RULE_V42[] struRule = new NET_VCA_ONE_RULE_V42[16]; /* 规则数组*/ + public short[] wRelSnapChan = new short[3]; //关联抓图通道,当主通道报警时,同时会上传关联通道的抓拍图片,0表示不关联,其他值为关联通道号 + public byte byTrackEnable; //是否启用跟踪 + public byte byRes2; + public NET_DVR_PTZ_POSITION struPTZPosition; //场景位置信息 + public short wTrackDuration; //跟踪持续时间,单位s + public short wIntervalTime; //单次报警间隔时间(秒)[1-7200](默认为600) + public short wHeightLimit;//目标检测高度下限(厘米)[0-250](默认为80cm),小于此高度的目标将不作为目标进行检测 + public byte[] byRes = new byte[58];//保留 +} + +public static final int CID_CODE_LEN = 4; +public static final int DEV_SERIAL_LEN = 9; +public static final int ACCOUNTNUM_LEN = 6; +public static final int ACCOUNTNUM_LEN_32 = 32; + +public static class NET_DVR_CID_ALARM extends Structure { + public int dwSize; + public byte[] sCIDCode = new byte[CID_CODE_LEN/*4*/]; //CID事件号 + public byte[] sCIDDescribe = new byte[NAME_LEN/*32*/]; //CID事件名 + public NET_DVR_TIME_EX struTriggerTime = new NET_DVR_TIME_EX(); //触发报警的时间点 + public NET_DVR_TIME_EX struUploadTime = new NET_DVR_TIME_EX(); //上传报警的时间点 + public byte[] sCenterAccount = new byte[ACCOUNTNUM_LEN/*6*/]; //中心帐号 + public byte byReportType; //见定义NET_DVR_ALARMHOST_REPORT_TYPE + public byte byUserType; //用户类型,0-网络用户 1-键盘用户,2-手机用户,3-系统用户 + public byte[] sUserName = new byte[NAME_LEN/*32*/]; //网络用户用户名 + public short wKeyUserNo; //键盘用户号 0xFFFF表示无效 + public byte byKeypadNo; //键盘号 0xFF表示无效 + public byte bySubSysNo; //子系统号 0xFF表示无效 + public short wDefenceNo; //防区号 0xFFFF表示无效 + public byte byVideoChanNo; //视频通道号 0xFF表示无效 + public byte byDiskNo; //硬盘号 0xFF表示无效 + public short wModuleAddr; //模块地址 0xFFFF表示无效 + public byte byCenterType; //0-无效, 1-中心账号(长度6),2-扩展的中心账号(长度9) + public byte byRes1; + public byte[] sCenterAccountV40 = new byte[ACCOUNTNUM_LEN_32/*32*/]; //中心账号V40,使用此字段时sCenterAccount无效 + public byte[] byDevSerialNo = new byte[DEV_SERIAL_LEN]; /*产品序列号*/ + public byte byRepeaterNo; //中继器号,为0无效 + public short wRemoteCtrllerUserNo; //遥控器用户号,为0无效 + public int dwIOTChannelNo; //IOT通道号 + public byte[] byRes2 = new byte[12]; +} + +//车牌识别结果子结构 +public static class NET_DVR_PLATE_INFO extends Structure +{ + public byte byPlateType; //车牌类型 + public byte byColor; //车牌颜色 + public byte byBright; //车牌亮度 + public byte byLicenseLen; //车牌字符个数 + public byte byEntireBelieve; //整个车牌的置信度,-100 + public byte byRegion; // 区域索引值 0-保留,1-欧洲(EU),2-俄语区域(ER),3-欧洲&俄罗斯(EU&CIS) ,4-中东(ME),0xff-所有 + public byte byCountry; // 国家索引值,参照枚举COUNTRY_INDEX(不支持"COUNTRY_ALL = 0xff, //ALL 全部") + public byte byArea; //区域(省份),各国家内部区域枚举,阿联酋参照 EMI_AREA + public byte byPlateSize; //车牌尺寸,0~未知,1~long, 2~short(中东车牌使用) + public byte[] byRes = new byte[15]; //保留 + public byte[] sPlateCategory = new byte[8];//车牌附加信息, 即中东车牌中车牌号码旁边的小字信息,(目前只有中东地区支持) + public int dwXmlLen; //XML报警信息长度 + public Pointer pXmlBuf; // XML报警信息指针,报警类型为 COMM_ITS_PLATE_RESUL时有效,其XML对应到EventNotificationAlert XML Block + public NET_VCA_RECT struPlateRect = new NET_VCA_RECT(); //车牌位置 + public byte[] sLicense = new byte[MAX_LICENSE_LEN]; //车牌号码,注:中东车牌需求把小字也纳入车牌号码,小字和车牌号中间用空格分隔 + public byte[] byBelieve = new byte[MAX_LICENSE_LEN]; //各个识别字符的置信度,如检测到车牌"浙A12345", 置信度为,20,30,40,50,60,70,则表示"浙"字正确的可能性只有%,"A"字的正确的可能性是% +} + + public static class NET_DVR_VEHICLE_INFO extends Structure + { + public int dwIndex; //车辆序号 + public byte byVehicleType; //车辆类型 0 表示其它车型,1 表示小型车,2 表示大型车 ,3表示行人触发 ,4表示二轮车触发 5表示三轮车触发(3.5Ver) + public byte byColorDepth; //车身颜色深浅 + public byte byColor; //车身颜色,参考VCR_CLR_CLASS + /*雷达异常状态: + 0~雷达正常, + 1~雷达故障 + 2~雷达一直发送某一个相同速度值 + 3~雷达送出数据为0 + 4~雷达送出数据过大或者过小 + */ + public byte byRadarState; + public short wSpeed; //单位km/h + public short wLength; //前一辆车的车身长度 + /*违规类型,0-正常,1-低速,2-超速,3-逆行,4-闯红灯,5-压车道线,6-不按导向,7-路口滞留, + 8-机占非,9-违法变道,10-不按车道 11-违反禁令,12-路口停车,13-绿灯停车, 14-未礼让行人(违法代码1357), + 15-违章停车,16-违章掉头,17-占用应急车道,18-禁右,19-禁左,20-压黄线,21-未系安全带,22-行人闯红灯,23-加塞,24-违法使用远光灯, + 25-驾驶时拨打接听手持电话,26-左转不让直行,27-右转不让左转,28-掉头不让直行,29-大弯小转, 30-闯绿灯,31-未带头盔, + 32-非机动车载人,33-非机动车占用机动车道,34-非机动车打伞棚, 35-黑烟车, 36-鸣笛*/ + public byte byIllegalType; + public byte byVehicleLogoRecog; //参考枚举类型 VLR_VEHICLE_CLASS + public byte byVehicleSubLogoRecog; //车辆品牌子类型识别;参考VSB_VOLKSWAGEN_CLASS等子类型枚举。 + public byte byVehicleModel; //车辆子品牌年款,0-未知,参考"车辆子品牌年款.xlsx" + public byte[] byCustomInfo = new byte[16]; //自定义信息 + public short wVehicleLogoRecog; //车辆主品牌,参考"车辆主品牌.xlsx" (该字段兼容byVehicleLogoRecog); + public byte byIsParking;//是否停车 0-无效,1-停车,2-未停车 + public byte byRes;//保留字节 + public int dwParkingTime; //停车时间,单位:s + public byte[] byRes3 = new byte[8]; +} + + //手动抓拍 + public static class NET_DVR_MANUALSNAP extends Structure + { + public byte byOSDEnable;//0-不关闭(默认),1-关闭 + public byte byLaneNo;//车道号, 范围为1-6,默认为1(抓拍机内部测试使用) + public byte byChannel;//通道号 + public byte[] byRes = new byte[21]; //保留 +} + +//交通抓拍结果信息 +public static class NET_DVR_PLATE_RESULT extends Structure +{ + public int dwSize; + public byte byResultType; + public byte byChanIndex; + public short wAlarmRecordID; + public int dwRelativeTime; + public byte[] byAbsTime = new byte[32]; + public int dwPicLen; + public int dwPicPlateLen; + public int dwVideoLen; + public byte byTrafficLight; + public byte byPicNum; + public byte byDriveChan; + public byte byVehicleType; + public int dwBinPicLen; + public int dwCarPicLen; + public int dwFarCarPicLen; + public Pointer pBuffer3; + public Pointer pBuffer4; + public Pointer pBuffer5; + public byte[] byRes3 = new byte[8]; + public NET_DVR_PLATE_INFO struPlateInfo; + public NET_DVR_VEHICLE_INFO struVehicleInfo; + public Pointer pBuffer1; + public Pointer pBuffer2; +} + +public static class NET_DVR_TIME_V30 extends Structure +{ + public short wYear; + public byte byMonth; + public byte byDay; + public byte byHour; + public byte byMinute; + public byte bySecond; + public byte byRes; + public short wMilliSec; + public byte[] byRes1 = new byte[2]; + + + } + +public static class NET_ITS_PICTURE_INFO extends Structure +{ + public int dwDataLen; + public byte byType; + public byte byDataType; + public byte byCloseUpType; + public byte byPicRecogMode; + public int dwRedLightTime; + public byte[] byAbsTime = new byte[32]; + public NET_VCA_RECT struPlateRect = new NET_VCA_RECT(); + public NET_VCA_RECT struPlateRecgRect = new NET_VCA_RECT(); + public Pointer pBuffer; + public int dwUTCTime;//UTC时间 + public byte byCompatibleAblity;//兼容能力字段,按位表示,值:0- 无效,1- 有效 + public byte byTimeDiffFlag; /*时差字段是否有效 0-时差无效, 1-时差有效 */ + public byte cTimeDifferenceH; /*与UTC的时差(小时),-12 ... +14, +表示东区,,byTimeDiffFlag为1时有效*/ + public byte cTimeDifferenceM; /*与UTC的时差(分钟),-30, 30, 45, +表示东区,byTimeDiffFlag为1时有效*/ + public byte[] byRes2 = new byte[4]; +} + +public static class NET_ITS_PLATE_RESULT extends Structure + { + public int dwSize; + public int dwMatchNo; + public byte byGroupNum; + public byte byPicNo; + public byte bySecondCam; + public byte byFeaturePicNo; + public byte byDriveChan; + public byte byVehicleType; + public byte byDetSceneID; + public byte byVehicleAttribute; + public short wIllegalType; + public byte[] byIllegalSubType = new byte[8]; + public byte byPostPicNo; + public byte byChanIndex; + public short wSpeedLimit; + public byte byChanIndexEx; //byChanIndexEx*256+byChanIndex表示真实通道号。 + public byte byRes2; + public NET_DVR_PLATE_INFO struPlateInfo = new NET_DVR_PLATE_INFO(); + public NET_DVR_VEHICLE_INFO struVehicleInfo = new NET_DVR_VEHICLE_INFO(); + public byte[] byMonitoringSiteID= new byte[48]; + public byte[] byDeviceID= new byte[48]; + public byte byDir; + public byte byDetectType; + public byte byRelaLaneDirectionType; + public byte byCarDirectionType; + public int dwCustomIllegalType; + public Pointer pIllegalInfoBuf; + public byte byIllegalFromatType; + public byte byPendant; + public byte byDataAnalysis; + public byte byYellowLabelCar; + public byte byDangerousVehicles; + public byte byPilotSafebelt; + public byte byCopilotSafebelt; + public byte byPilotSunVisor; + public byte byCopilotSunVisor; + public byte byPilotCall; + public byte byBarrierGateCtrlType; + public byte byAlarmDataType; + public NET_DVR_TIME_V30 struSnapFirstPicTime = new NET_DVR_TIME_V30(); + public int dwIllegalTime; + public int dwPicNum; + public NET_ITS_PICTURE_INFO[] struPicInfo= new NET_ITS_PICTURE_INFO[6]; +} + +public int MAX_PARKNO_LEN = 16; //车位编号长度 +public int MAX_ID_LEN = 48; //编号最大长度 + +//停车场数据上传 +public static class NET_ITS_PARK_VEHICLE extends Structure +{ + public int dwSize; //结构长度 + public byte byGroupNum; //图片组数量(单次轮询抓拍的图片数量) + public byte byPicNo; //连拍的图片组上传图片序号(接收到图片组数量后,表示接收完成 + //接收超时不足图片组数量时,根据需要保留或删除) + public byte byLocationNum; //单张图片所管理的车位数 + public byte byParkError; //停车异常,0-正常 1 异常 + public byte[] byParkingNo = new byte[MAX_PARKNO_LEN];//车位编号 + public byte byLocationStatus; //车位车辆状态,0-无车,1有车 + public byte bylogicalLaneNum;//逻辑车位号,0-3,一个相机最大能管4个车位 (0代表最左边,3代表最右边) + public short wUpLoadType;//第零位表示:0~轮训上传、1~变化上传 + public byte[] byRes1 = new byte[4]; //保留字节 + public int dwChanIndex; //通道号数字通道 + public NET_DVR_PLATE_INFO struPlateInfo; //车牌信息结构 + public NET_DVR_VEHICLE_INFO struVehicleInfo; //车辆信息 + public byte[] byMonitoringSiteID = new byte[MAX_ID_LEN]; //监测点编号 + public byte[] byDeviceID = new byte[MAX_ID_LEN]; //设备编号 + public int dwPicNum; //图片数量(与picGroupNum不同,代表本条信息附带的图片数量,图片信息由struVehicleInfoEx定义 + public NET_ITS_PICTURE_INFO[] struPicInfo = new NET_ITS_PICTURE_INFO[2]; //图片信息,单张回调,最多2张图,由序号区分 + public byte[] byRes2 = new byte[256]; +} + +public static class NET_DVR_SNAPCFG extends Structure { + + public int dwSize; + public byte byRelatedDriveWay;//触发IO关联的车道号 + public byte bySnapTimes; //线圈抓拍次数,0-不抓拍,非0-连拍次数,目前最大5次 + public short wSnapWaitTime; //抓拍等待时间,单位ms,取值范围[0,60000] + public short[] wIntervalTime = new short[MAX_INTERVAL_NUM];//连拍间隔时间,ms + public int dwSnapVehicleNum; //抓拍车辆序号。 + public NET_DVR_JPEGPARA struJpegPara;//抓拍图片参数 + public byte[] byRes2 = new byte[16]; +} + +// 道闸控制 +public static class NET_DVR_BARRIERGATE_CFG extends Structure { + public int dwSize; + public int dwChannel; //通道号 + public byte byLaneNo; //道闸号(0-表示无效值(设备需要做有效值判断),1-道闸1) + /* + 若老的平台不支持byUnlock字段,该字段将赋值为0,通过“0-关闭道闸,1-开启道闸,2-停止道闸”中的任何一种操作皆可进行解锁。 + 若新平台支持byUnlock字段,需byUnlock字段赋值为1,并结合4~解锁道闸来进行解锁。byUnlock字段赋值为1后,“0-关闭道闸,1-开启道闸,2-停止道闸”操作将不可用于解锁。 + */ + public byte byBarrierGateCtrl;//0-关闭道闸,1-开启道闸,2-停止道闸 3-锁定道闸,4~解锁道闸 + public byte byEntranceNo;//出入口编号 [1,8] + public byte byUnlock;//启用解锁使能,0~为不启用,1~启用 + public byte[] byRes = new byte[12]; +} + + + +public static class NET_DVR_AGEGROUP_PARAM extends Structure { + public int dwTeenage;//少年(人数) + public int dwYouth;//青年(人数) + public int dwMidLife;//中年(人数) + public int dwElderly;//老年(人数) + public int dwChild;//儿童(人数) + public int dwAdolescent;//青少年(人数) + public int dwPrime;//壮年(人数) + public int dwMidage;//中老年(人数) + public byte[] byRes = new byte[48]; +} + +public static class NET_DVR_SEXGROUP_PARAM extends Structure { + public int dwMale;//男(人数) + public int dwFemale;//女(人数) + public byte[] byRes = new byte[64]; +} + +public static class NET_DVR_PROGRAM_INFO extends Structure { + public int dwProgramNo; //节目编号 + public byte[] sProgramName = new byte[NAME_LEN]; //节目名称 + public byte[] byRes= new byte[16]; +} + +public static class NET_DVR_FACECAPTURE_STATISTICS_RESULT extends Structure { + public int dwSize; + public NET_DVR_TIME_EX struStartTime;/*间隔开始时间*/ + public NET_DVR_TIME_EX struEndTime;/*间隔结束时间*/ + public byte byStatType;//数据类型统计:Bit0-年龄段有效,Bit1-性别有效,Bit2-人数有效 + public byte[] byRes = new byte[7]; + public int dwPeopleNum;//人数统计 + public NET_DVR_AGEGROUP_PARAM struAgeGroupParam;//年龄段人数统计 + public NET_DVR_SEXGROUP_PARAM struSexGroupParam;//性别人数统计 + public NET_DVR_PROGRAM_INFO struProgramInfo; //节目信息 + public byte[] byRes1= new byte[76]; +} + +//获取交通数据条件结构 +public static class NET_DVR_TRAFFIC_DATA_QUERY_COND extends Structure { + public int dwSize; + /* + Bit0-通道有效 + Bit1-时间有效 + Bit2-车牌号有效 + Bit3-车牌类型有效 + Bit4-车牌颜色有效 + Bit5-车身颜色有效 + Bit6-车辆类型有效 + Bit7-车辆品牌有效 + Bit8-车道号有效 + Bit9-监测方向有效 + Bit10-最低速度有效 + Bit11-最高速度有效 + Bit12-数据类型有效 + Bit13-布控方式类型有效 + Bit14-违法取证有效 + Bit15-事件类型有效 + Bit16-取证类型有效 + */ + public int dwQueryCond;//查询条件 0表示无效,1表示有效 + public int dwChannel;//默认是1([1~32],bit0表示通道1,依次类推bit31表示通道32) + public NET_DVR_TIME_V30 struStartTime;//开始时间 + public NET_DVR_TIME_V30 struEndTime;//结束时间 + public byte[] sLicense= new byte[MAX_LICENSE_LEN/*16*/];//(设备支持模糊查询, GB2312编码) + /* + Bit0-未知(其他) + Bit1-标准民用车与军车 + Bit2-02式民用车牌 + Bit3-武警车 + Bit4-警车 + Bit5-民用车双行尾牌 + Bit6-使馆车牌 + Bit7-农用车 + Bit8-摩托车 + */ + public int dwPlateType;//车牌类型(支持按位表示,可以复选) + /* + Bit0-未知(其他) + Bit1-黄色 + Bit2-白色 + Bit3-黑色 + Bit4-绿色 + Bit5-蓝色 + */ + public int dwPlateColor;//车牌颜色(支持按位表示,可以复选) + /* + Bit0-未?知(其他) + Bit1-白色 + Bit2-银色 + Bit3-灰色 + Bit4-黑色 + Bit5-红色 + Bit6-深蓝色 + Bit7-蓝色 + Bit8-黄色 + Bit9-绿色 + Bit10-棕色 + Bit11-粉色 + Bit12-紫色 + Bit13-深灰色 + */ + public int dwVehicleColor;//车身颜色(支持按位表示,可以复选) + /* + Bit0-未知(其他) + Bit1-客车 + Bit2-大货车 + Bit3-轿车 + Bit4-面包车 + Bit5-小货车 + Bit6-行人 + Bit7-二轮车 + Bit8-三轮车 + Bit9-SUV/MPV + Bit10-中型客车 + */ + public int dwVehicleType;//车辆类型(支持按位表示,可以复选) + /** + Bit0-其他(保留) + Bit1-低速 + Bit2-超速 + Bit3-逆行 + Bit4-闯红灯 + Bit5-压车道线 + Bit6-不按导向 + Bit7-路口滞留 + Bit8-机占非 + Bit9-违法变道 + Bit10-不按车道 + Bit11-违反禁令 + Bit12-路口停车 + Bit13-绿灯停车 + Bit14-未礼让行人 + Bit15-违章停车 + Bit16-违章掉头 + Bit17-占用应急车道 + Bit18-未系安全带 + */ + public int dwIllegalType; + /** + Bit0-其他(保留) + Bit1-拥堵 + Bit2-停车 + Bit3-逆行 + Bit4-行人 + Bit5-抛洒物 + Bit6-烟雾 + Bit7-压线 + Bit8-黑名单 + Bit9-超速 + Bit10-变道 + Bit11-掉头 + Bit12-机占非 + Bit13-加塞 + */ + public int dwEventType; + /** + Bit0-其他(保留) + Bit1-城市公路违法停车 + Bit2-高速公路违法停车 + Bit3-压线 + Bit4-逆行 + Bit5-违法变道 + Bit6-机占非 + */ + public int dwForensiceType; + public short wVehicleLogoRecog; //车辆主品牌,参考"车辆主品牌.xlsx" (仅单选) + public byte byLaneNo;//车道号(0~255,0号车道 表示 车道号未知) + public byte byDirection;//监测方向,1-上行,2-下行,3-双向,4-由东向西,5-由南向北,6-由西向东,7-由北向南 + public short wMinSpeed;//最低速度(0~999)单位km/h + public short wMaxSpeed;//最高速度(0~999)单位km/h + public byte byDataType;//数据类型 0-卡口数据,1-违法数据,2-交通事件,3-取证数据 (仅单选) + public byte byExecuteCtrl;//布控 0-白名单,1-黑名单,0xff-其他 + public byte[] byRes = new byte[254]; +} + +public static final int MAX_TRAFFIC_PICTURE_NUM = 8; //交通图片数量 +//交通数据结构体 +public static class NET_DVR_TRAFFIC_DATA_QUERY_RESULT extends Structure +{ + public int dwSize; + public int dwChannel;//默认是1([1~32]) + public byte[] sLicense = new byte[MAX_LICENSE_LEN/*16*/]; + /* + Bit0-未知(其他) + Bit1-标准民用车与军车 + Bit2-02式民用车牌 + Bit3-武警车 + Bit4-警车 + Bit5-民用车双行尾牌 + Bit6-使馆车牌 + Bit7-农用车 + Bit8-摩托车 + */ + public int dwPlateType;//车牌类型 + /* + Bit0-未知(其他) + Bit1-黄色 + Bit2-白色 + Bit3-黑色 + Bit4-绿色 + Bit5-蓝色 + */ + public int dwPlateColor;//车牌颜色 + /* + Bit0-未知(其他) + Bit1-白色 + Bit2-银色 + Bit3-灰色 + Bit4-黑色 + Bit5-红色 + Bit6-深蓝色 + Bit7-蓝色 + Bit8-黄色 + Bit9-绿色 + Bit10-棕色 + Bit11-粉色 + Bit12-紫色 + Bit13-深灰色 + */ + public int dwVehicleColor;//车身颜色 + /* + Bit0-未知(其他) + Bit1-客车 + Bit2-大货车 + Bit3-轿车 + Bit4-面包车 + Bit5-小货车 + Bit6-行人 + Bit7-二轮车 + Bit8-三轮车 + Bit9-SUV/MPV + Bit10-中型客车 + Bit11-机动车 + Bit12-非机动车 + Bit13-小型轿车 + Bit14-微型轿车 + Bit15-皮卡车 + Bit16-集装箱卡车 + Bit17-微卡,栏板卡 + Bit18-渣土车 + Bit19-吊车,工程车 + Bit20-油罐车 + Bit21-混凝土搅拌车 + Bit22-平板拖车 + Bit23-两厢轿车 + Bit24-三厢轿车 + Bit25-轿跑 + Bit26-小型客车 + */ + public int dwVehicleType;//车辆类型 + /** + Bit0-其他(保留) + Bit1-低速 + Bit2-超速 + Bit3-逆行 + Bit4-闯红灯 + Bit5-压车道线 + Bit6-不按导向 + Bit7-路口滞留 + Bit8-机占非 + Bit9-违法变道 + Bit10-不按车道 + Bit11-违反禁令 + Bit12-路口停车 + Bit13-绿灯停车 + Bit14-未礼让行人 + Bit15-违章停车 + Bit16-违章掉头 + Bit17-占用应急车道 + Bit18-未系安全带 + */ + public int dwIllegalType; + /** + Bit0-其他(保留) + Bit1-拥堵 + Bit2-停车 + Bit3-逆行 + Bit4-行人 + Bit5-抛洒物 + Bit6-烟雾 + Bit7-压线 + Bit8-黑名单 + Bit9-超速 + Bit10-变道 + Bit11-掉头 + Bit12-机占非 + Bit13-加塞 + */ + public int dwEventType; + /** + Bit0-其他(保留) + Bit1-城市公路违法停车 + Bit2-高速公路违法停车 + Bit3-压线 + Bit4-逆行 + Bit5-违法变道 + Bit6-机占非 + */ + public int dwForensiceType; + public short wVehicleLogoRecog; //车辆主品牌,参考"车辆主品牌.xlsx" + public byte byLaneNo;//车道号(0~255,0号车道 表示 车道号未知) + public byte byDirection;//监测方向,1-上行,2-下行,3-双向,4-由东向西,5-由南向北,6-由西向东,7-由北向南 + public short wSpeed;//速度(0~999)单位km/h + public byte byDataType;//数据类型: 0-卡口 1-违法 2-事件 3-取证 + public byte[] byRes = new byte[253]; + public NET_DVR_TRAFFIC_PICTURE_PARAM[] struTrafficPic = new NET_DVR_TRAFFIC_PICTURE_PARAM[MAX_TRAFFIC_PICTURE_NUM/*8*/]; +} + +//交通图片参数子结构 +public static final int PICTURE_NAME_LEN = 64; +public static class NET_DVR_TRAFFIC_PICTURE_PARAM extends Structure +{ + public NET_DVR_TIME_V30 struRelativeTime = new NET_DVR_TIME_V30(); //抓拍相对时标 + public NET_DVR_TIME_V30 struAbsTime = new NET_DVR_TIME_V30(); //抓拍绝对时标 + public byte[] szPicName = new byte[PICTURE_NAME_LEN/*64*/]; + public byte byPicType;//图片类型 0-车牌图,1-抓拍原图,2-合成图,3-特写图 + public byte[] byRes = new byte[63]; +} + +public static class NET_DVR_VEHICLE_CONTROL_COND extends Structure{ + public int dwChannel; + public int dwOperateType; + public byte[] sLicense = new byte[MAX_LICENSE_LEN]; + public byte[] sCardNo = new byte[48]; + public byte byListType; + public byte[] byRes1 = new byte[3]; + public int dwDataIndex; + public byte[] byRes = new byte[116]; +} +public static class NET_DVR_VEHICLE_CONTROL_LIST_INFO extends Structure{ + public int dwSize; + public int dwChannel; + public int dwDataIndex; + public byte[] sLicense=new byte[16]; + public byte byListType; + public byte byPlateType; + public byte byPlateColor; + public byte[] byRes = new byte[21]; + public byte[] sCardNo = new byte[48]; + public NET_DVR_TIME_V30 struStartTime = new NET_DVR_TIME_V30(); + public NET_DVR_TIME_V30 struStopTime = new NET_DVR_TIME_V30(); + public byte[] sOperateIndex=new byte[32]; + public byte[] byRes1= new byte[224]; +} + +//车辆报警(黑白名单) +public static class NET_DVR_VEHICLE_CONTROL_ALARM extends Structure{ + public int dwSize; + public byte byListType; //名单属性(黑白名单)0-白名单,1-黑名单,2-临时名单 + public byte byPlateType; //车牌类型 + public byte byPlateColor; //车牌颜色 + public byte byRes1; + public byte[] sLicense = new byte[MAX_LICENSE_LEN];//车牌号码 + public byte[] sCardNo = new byte[MAX_CARDNO_LEN]; // 卡号 + public NET_DVR_TIME_V30 struAlarmTime = new NET_DVR_TIME_V30(); //报警时间 + public int dwChannel; //设备通道号,如果直连的是IPC,则为ipc通道号;如果连的DVR\nvr,则为DVR\NVR的通道号 + public int dwPicDataLen; //图片数据大小,0表示无图片,不为0是表示后面带图片数据 + public byte byPicType; //图片类型,0-JPEG + public byte byPicTransType; //图片数据传输方式: 0-二进制;1-url + public byte[] byRes3 = new byte[2]; + public Pointer pPicData; + public byte[] byRes2 = new byte[48]; +} + +public int MAX_LED_INFO_LEN = 512; +public int MAX_VOICE_INFO_LEN = 128; + +//LED屏幕显示参数 +public static class NET_DVR_LEDDISPLAY_CFG extends Structure { + public int dwSize;//结构体大小 + public byte[] sDisplayInfo = new byte[MAX_LED_INFO_LEN/*512*/]; // LED显示内容 + public byte byDisplayMode;//显示方式:0~左移,1~右移,2~立即显示 + public byte bySpeedType;//速度类型:0~快,1~中,2~慢 + public byte byShowPlateEnable;//显示车牌使能,0~关闭,1~启用 + public byte byRes1; + public int dwShowTime;//显示时长,1~60秒 + public byte[] byRes = new byte[128]; +} + +//语音播报控制参数 +public static class NET_DVR_VOICEBROADCAST_CFG extends Structure { + public int dwSize;//结构体大小 + public byte[] sInfo = new byte[MAX_VOICE_INFO_LEN/*128*/]; //语音播报内容 + public byte byBroadcastNum;// 语音播报次数, 1~10次 + public byte byIntervalTime;// 语音播报间隔时间,1~5s + public byte[] byRes = new byte[126]; +} + +//缴费金额信息 +public static class NET_DVR_CHARGEACCOUNT_CFG extends Structure { + public int dwSize;//结构体大小 + public float fAccount;//实际收费金额 + public byte[] byRes = new byte[128]; +} + +public static final int DOOR_NAME_LEN = 32; //门名称 +public static final int STRESS_PASSWORD_LEN = 8; //胁迫密码长度 +public static final int SUPER_PASSWORD_LEN = 8; //胁迫密码长度 +public static final int UNLOCK_PASSWORD_LEN = 8; // 解除密码长度 + +public static class NET_DVR_DOOR_CFG extends Structure +{ + public int dwSize; + public byte[] byDoorName = new byte[DOOR_NAME_LEN]; //门名称 + public byte byMagneticType; //门磁类型,0-常闭,1-常开 + public byte byOpenButtonType; //开门按钮类型,0-常闭,1-常开 + public byte byOpenDuration; //开门持续时间,1-255s(楼层继电器动作时间) + public byte byDisabledOpenDuration; //残疾人卡开门持续时间,1-255s + public byte byMagneticAlarmTimeout; //门磁检测超时报警时间,0-255s,0表示不报警 + public byte byEnableDoorLock; //是否启用闭门回锁,0-否,1-是 + public byte byEnableLeaderCard; //是否启用首卡常开功能,0-否,1-是 + public byte byLeaderCardMode; //首卡模式,0-不启用首卡功能,1-首卡常开模式,2-首卡授权模式(使用了此字段,则byEnableLeaderCard无效) + public int dwLeaderCardOpenDuration; //首卡常开持续时间,1-1440min + public byte[] byStressPassword = new byte[STRESS_PASSWORD_LEN]; //胁迫密码 + public byte[] bySuperPassword = new byte[SUPER_PASSWORD_LEN]; //超级密码 + public byte[] byUnlockPassword = new byte[UNLOCK_PASSWORD_LEN]; //解除码NET_DVR_LOCAL_CONTROLLER_STATUS + public byte byUseLocalController; //只读,是否连接在就地控制器上,0-否,1-是 + public byte byRes1; + public short wLocalControllerID; //只读,就地控制器序号,1-64,0代表未注册 + public short wLocalControllerDoorNumber; //只读,就地控制器的门编号,1-4,0代表未注册 + public short wLocalControllerStatus; //只读,就地控制器在线状态:0-离线,1-网络在线,2-环路1上的RS485串口1,3-环路1上的RS485串口2,4-环路2上的RS485串口1,5-环路2上的RS485串口2,6-环路3上的RS485串口1,7-环路3上的RS485串口2,8-环路4上的RS485串口1,9-环路4上的RS485串口2(只读) + public byte byLockInputCheck; //是否启用门锁输入检测(1字节,0不启用,1启用,默认不启用) + public byte byLockInputType; //门锁输入类型(1字节,0常闭,1常开,默认常闭) + public byte byDoorTerminalMode; //门相关端子工作模式(1字节,0防剪防短,1普通,默认防剪防短) + public byte byOpenButton; //是否启用开门按钮(1字节,0是,1否,默认是) + public byte byLadderControlDelayTime; //梯控访客延迟时间,1-255min + public byte[] byRes2 = new byte[43]; +} + +public static class NET_DVR_DOOR_STATUS_PLAN extends Structure +{ + public int dwSize; + public int dwTemplateNo; //计划模板编号,为0表示取消关联,恢复默认状态(普通状态) + public byte[] byRes = new byte[64]; +} + + +public static class NET_DVR_EVENT_CARD_LINKAGE_COND extends Structure +{ + public int dwSize; + public int dwEventID; //事件ID + public short wLocalControllerID; //就地控制器序号[1,64] + public byte[] byRes = new byte[106]; +} + +public static final int MAX_ALARMHOST_ALARMIN_NUM = 512;//网络报警主机最大报警输入口数 +public static final int MAX_ALARMHOST_ALARMOUT_NUM = 512;//网络报警主机最大报警输出口数 + +public static class NET_DVR_EVENT_CARD_LINKAGE_CFG_V50 extends Structure +{ + public int dwSize; //结构体大小 + public byte byProMode; //联动方式,0-事件,1-卡号, 2-MAC地址 + public byte[] byRes1 = new byte[3]; + public int dwEventSourceID; //事件源ID,当主类型为设备事件时无效, 当主类型是门事件时为门编号;当主类型为读卡器事件时,为读卡器ID;当为报警输入事件时为防区报警输入ID或事件报警输入ID。0xffffffff表示联动全部 + public NET_DVR_EVETN_CARD_LINKAGE_UNION uLinkageInfo = new NET_DVR_EVETN_CARD_LINKAGE_UNION(); //联动方式参数 + public byte[] byAlarmout = new byte[MAX_ALARMHOST_ALARMOUT_NUM]; //关联的报警输出号,按位表示,为0表示不关联,为1表示关联 + public byte[] byRes2 = new byte[32]; //保留 + public byte[] byOpenDoor = new byte[MAX_DOOR_NUM_256]; //按位表示,是否联动开门,0-不联动,1-联动 + public byte[] byCloseDoor = new byte[MAX_DOOR_NUM_256]; //按位表示,是否联动关门,0-不联动,1-联动 + public byte[] byNormalOpen = new byte[MAX_DOOR_NUM_256]; //按位表示,是否联动常开,0-不联动,1-联动 + public byte[] byNormalClose = new byte[MAX_DOOR_NUM_256]; //按位表示,是否联动常关,0-不联动,1-联动 + public byte byMainDevBuzzer; //主机蜂鸣器 0-不联动,1-联动输出 + public byte byCapturePic; //是否联动抓拍,0-不联动抓拍,1-联动抓拍 + public byte byRecordVideo; //是否联动录像,0-不联动录像,1-联动录像 + public byte[] byRes3 = new byte[29]; //保留 + public byte[] byReaderBuzzer = new byte[MAX_CARD_READER_NUM_512]; //联动读卡器蜂鸣器,按位表示,0-不联动,1-联动 + public byte[] byAlarmOutClose = new byte[MAX_ALARMHOST_ALARMOUT_NUM]; //关联报警输出关闭,按字节表示,为0表示不关联,为1表示关联 + public byte[] byAlarmInSetup = new byte[MAX_ALARMHOST_ALARMIN_NUM]; //关联防区布防,按字节表示,为0表示不关联,为1表示关联 + public byte[] byAlarmInClose = new byte[MAX_ALARMHOST_ALARMIN_NUM]; //关联防区撤防,按字节表示,为0表示不关联,为1表示关联 + public byte[] byRes = new byte[500]; //保留 +} + +public static class NET_DVR_EVENT_LINKAGE_INFO extends Structure +{ + public short wMainEventType; //事件主类型,0-设备事件,1-报警输入事件,2-门事件,3-读卡器事件 + public short wSubEventType; //事件次类型 + public byte[] byRes = new byte[28]; +} + +public static class NET_DVR_EVETN_CARD_LINKAGE_UNION extends Union{ + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //卡号 + public NET_DVR_EVENT_LINKAGE_INFO struEventLinkage; //事件联动时参数 + public byte[] byMACAddr = new byte[MACADDR_LEN]; //物理MAC地址 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID) +} + +//卡参数配置条件 +public static class NET_DVR_CARD_CFG_COND extends Structure { + public int dwSize; + public int dwCardNum; + public byte byCheckCardNo; + public byte[] ibyRes = new byte[31]; +} + +//获取卡参数的发送数据 +public static class NET_DVR_CARD_CFG_SEND_DATA extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[32]; + public byte[] byRes = new byte[16]; +} + +public static class CARDRIGHTPLAN extends Structure { + public byte[] byRightPlan = new byte[4]; +} + +//卡参数 +public static class NET_DVR_CARD_CFG extends Structure { + public int dwSize; + public int dwModifyParamType; + public byte[] byCardNo = new byte[32]; + public byte byCardValid; + public byte byCardType; + public byte byLeaderCard; + public byte byRes1; + public int dwDoorRight; + public NET_DVR_VALID_PERIOD_CFG struValid; + public int dwBelongGroup; + public byte[] byCardPassword = new byte[8]; + public CARDRIGHTPLAN[] byCardRightPlan = new CARDRIGHTPLAN[32]; + public int dwMaxSwipeTime; + public int dwSwipeTime; + public short wRoomNumber; + public short wFloorNumber; + public byte[] byRes2 = new byte[20]; +} + +public int ACS_CARD_NO_LEN = 32; //门禁卡号长度 +public int MAX_GROUP_NUM_128 = 128; //最大群组数 +public int MAX_DOOR_NUM_256 = 256; //最大门数 +public int CARD_PASSWORD_LEN = 8; //卡密码长度 +public int MAX_CARD_READER_NUM = 64; //最大读卡器数 +public int MAX_DOOR_CODE_LEN = 8; //房间代码长度 +public int MAX_LOCK_CODE_LEN = 8; //锁代码长度 +public int MAX_CARD_RIGHT_PLAN_NUM = 4; //卡权限最大计划个数 + +public static class CARDRIGHTPLAN_WORD extends Structure { + public short[] wRightPlan = new short[MAX_CARD_RIGHT_PLAN_NUM]; +} + +public static class NET_DVR_CARD_CFG_V50 extends Structure { + public int dwSize; + public int dwModifyParamType;//需要修改的卡参数,设置卡参数时有效,按位表示,每位代表一种参数,1为需要修改,0为不修改 + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //卡号 + public byte byCardValid; //卡是否有效,0-无效,1-有效(用于删除卡,设置时置为0进行删除,获取时此字段始终为1) + public byte byCardType; //卡类型,1-普通卡,2-残疾人卡,3-黑名单卡,4-巡更卡,5-胁迫卡,6-超级卡,7-来宾卡,8-解除卡,9-员工卡,10-应急卡,11-应急管理卡,默认普通卡 + public byte byLeaderCard; //是否为首卡,1-是,0-否 + public byte byRes1; + public byte[] byDoorRight = new byte[MAX_DOOR_NUM_256]; //门权限(楼层权限),按位表示,1为有权限,0为无权限,从低位到高位表示对门1-N是否有权限 + public NET_DVR_VALID_PERIOD_CFG struValid; //有效期参数 + public byte[] byBelongGroup = new byte[MAX_GROUP_NUM_128]; //所属群组,按字节表示,1-属于,0-不属于 + public byte[] byCardPassword = new byte[CARD_PASSWORD_LEN]; //卡密码 + public CARDRIGHTPLAN_WORD[] wCardRightPlan = new CARDRIGHTPLAN_WORD[MAX_DOOR_NUM_256]; //卡权限计划,取值为计划模板编号,同个门不同计划模板采用权限或的方式处理 + public int dwMaxSwipeTime; //最大刷卡次数,0为无次数限制(开锁次数) + public int dwSwipeTime; //已刷卡次数 + public short wRoomNumber; //房间号 + public short wFloorNumber; //层号 + public int dwEmployeeNo; //工号 + public byte[] byName = new byte[NAME_LEN]; //姓名 + public short wDepartmentNo; //部门编号 + public short wSchedulePlanNo; //排班计划编号 + public byte bySchedulePlanType; //排班计划类型:0-无意义、1-个人、2-部门 + public byte byRightType; //下发权限类型:0-普通发卡权限、1-二维码权限、2-蓝牙权限(可视对讲设备二维码权限配置项:房间号、卡号(虚拟卡号)、最大刷卡次数(开锁次数)、有效期参数;蓝牙权限:卡号(萤石APP账号)、其他参数配置与普通发卡权限一致) + public byte[] byRes2 = new byte[2]; + public int dwLockID; //锁ID + public byte[] byLockCode = new byte[MAX_LOCK_CODE_LEN]; //锁代码 + public byte[] byRoomCode = new byte[MAX_DOOR_CODE_LEN]; //房间代码 + public int dwCardRight; //卡权限 + public int dwPlanTemplate; //计划模板(每天)各时间段是否启用,按位表示,0--不启用,1-启用 + public int dwCardUserId; //持卡人ID + public byte byCardModelType; //0-空,1- MIFARE S50,2- MIFARE S70,3- FM1208 CPU卡,4- FM1216 CPU卡,5-国密CPU卡,6-身份证,7- NFC + public byte[] byRes3 = new byte[83]; +} + +//有效期参数结构体 +public static class NET_DVR_VALID_PERIOD_CFG extends Structure { + public byte byEnable; + public byte[] byRes1 = new byte[3]; + public NET_DVR_TIME_EX struBeginTime; + public NET_DVR_TIME_EX struEndTime; + public byte[] byRes2 = new byte[32]; +} + +//扩展结构体信息 +public static class NET_DVR_ID_CARD_INFO_EXTEND extends Structure { + public byte byRemoteCheck; //是否需要远程核验(0-无效,1-不需要(默认),2-需要) + public byte byThermometryUnit; //测温单位(0-摄氏度(默认),1-华氏度,2-开尔文) + public byte byIsAbnomalTemperature; //人脸抓拍测温是否温度异常:1-是,0-否 + public byte byRes2; + public float fCurrTemperature; //人脸温度(精确到小数点后一位) + public NET_VCA_POINT struRegionCoordinates = new NET_VCA_POINT(); //人脸温度坐标 + public int dwQRCodeInfoLen; //二维码信息长度,不为0是表示后面带数据 + public int dwVisibleLightDataLen; //热成像相机可见光图片长度,不为0是表示后面带数据 + public int dwThermalDataLen; //热成像图片长度,不为0是表示后面带数据 + public Pointer pQRCodeInfo; //二维码信息指针 + public Pointer pVisibleLightData; //热成像相机可见光图片指针 + public Pointer pThermalData; //热成像图片指针 + public byte[] byRes = new byte[1024]; + +} + +//身份证信息报警 +public static class NET_DVR_ID_CARD_INFO_ALARM extends Structure { + public int dwSize; //结构长度 + public NET_DVR_ID_CARD_INFO struIDCardCfg = new NET_DVR_ID_CARD_INFO();//身份证信息 + public int dwMajor; //报警主类型,参考宏定义 + public int dwMinor; //报警次类型,参考宏定义 + public NET_DVR_TIME_V30 struSwipeTime = new NET_DVR_TIME_V30(); //时间 + public byte[] byNetUser = new byte[MAX_NAMELEN] ;//网络操作的用户名 + public NET_DVR_IPADDR struRemoteHostAddr = new NET_DVR_IPADDR();//远程主机地址 + public int dwCardReaderNo; //读卡器编号,为0无效 + public int dwDoorNo; //门编号,为0无效 + public int dwPicDataLen; //图片数据大小,不为0是表示后面带数据 + public Pointer pPicData; + public byte byCardType; //卡类型,1-普通卡,2-残疾人卡,3-黑名单卡,4-巡更卡,5-胁迫卡,6-超级卡,7-来宾卡,8-解除卡,为0无效 + public byte byDeviceNo; // 设备编号,为0时无效(有效范围1-255) + public byte byMask; //是否带口罩:0-保留,1-未知,2-不戴口罩,3-戴口罩 + public byte byRes2; + public int dwFingerPrintDataLen; // 指纹数据大小,不为0是表示后面带数据 + public Pointer pFingerPrintData; + public int dwCapturePicDataLen; // 抓拍图片数据大小,不为0是表示后面带数据 + public Pointer pCapturePicData; + public int dwCertificatePicDataLen; //证件抓拍图片数据大小,不为0是表示后面带数据 + public Pointer pCertificatePicData; + public byte byCardReaderKind; //读卡器属于哪一类,0-无效,1-IC读卡器,2-身份证读卡器,3-二维码读卡器,4-指纹头 + public byte[] byRes3 = new byte[2]; + public byte byIDCardInfoExtend; //pIDCardInfoExtend是否有效:0-无效,1-有效 + public Pointer pIDCardInfoExtend; //byIDCardInfoExtend为1时,表示指向一个NET_DVR_ID_CARD_INFO_EXTEND结构体 + public byte[] byRes = new byte[172]; +} + +public static final int CARD_READER_DESCRIPTION = 32; //读卡器描述 + +public static class NET_DVR_CARD_READER_CFG_V50 extends Structure { + public int dwSize; + public byte byEnable; //是否使能,1-使能,0-不使能 + public byte byCardReaderType; //读卡器类型,1-DS-K110XM/MK/C/CK,2-DS-K192AM/AMP,3-DS-K192BM/BMP,4-DS-K182AM/AMP,5-DS-K182BM/BMP,6-DS-K182AMF/ACF,7-韦根或485不在线,8- DS-K1101M/MK,9- DS-K1101C/CK,10- DS-K1102M/MK/M-A,11- DS-K1102C/CK,12- DS-K1103M/MK,13- DS-K1103C/CK,14- DS-K1104M/MK,15- DS-K1104C/CK,16- DS-K1102S/SK/S-A,17- DS-K1102G/GK,18- DS-K1100S-B,19- DS-K1102EM/EMK,20- DS-K1102E/EK,21- DS-K1200EF,22- DS-K1200MF,23- DS-K1200CF,24- DS-K1300EF,25- DS-K1300MF,26- DS-K1300CF,27- DS-K1105E,28- DS-K1105M,29- DS-K1105C,30- DS-K182AMF,31- DS-K196AMF,32-DS-K194AMP,33-DS-K1T200EF/EF-C/MF/MF-C/CF/CF-C,34-DS-K1T300EF/EF-C/MF/MF-C/CF/CF-C,35-DS-K1T105E/E-C/M/M-C/C/C-C,36-DS-K1T803F/F-M/F-S/F-E,37-DS-K1A801F/F-M/F-S/F-E,38-DS-K1107M/MK,39-DS-K1107E/EK,40-DS-K1107S/SK,41-DS-K1108M/MK,42-DS-K1108E/EK,43-DS-K1108S/SK,44-DS-K1200F,45-DS-K1S110-I,46-DS-K1T200M-PG/PGC,47-DS-K1T200M-PZ/PZC,48-DS-K1109H + public byte byOkLedPolarity; //OK LED极性,0-阴极,1-阳极 + public byte byErrorLedPolarity; //Error LED极性,0-阴极,1-阳极 + public byte byBuzzerPolarity; //蜂鸣器极性,0-阴极,1-阳极 + public byte bySwipeInterval; //重复刷卡间隔时间,单位:秒 + public byte byPressTimeout; //按键超时时间,单位:秒 + public byte byEnableFailAlarm; //是否启用读卡失败超次报警,0-不启用,1-启用 + public byte byMaxReadCardFailNum; //最大读卡失败次数 + public byte byEnableTamperCheck; //是否支持防拆检测,0-disable ,1-enable + public byte byOfflineCheckTime; //掉线检测时间 单位秒 + public byte byFingerPrintCheckLevel; //指纹识别等级,1-1/10误认率,2-1/100误认率,3-1/1000误认率,4-1/10000误认率,5-1/100000误认率,6-1/1000000误认率,7-1/10000000误认率,8-1/100000000误认率,9-3/100误认率,10-3/1000误认率,11-3/10000误认率,12-3/100000误认率,13-3/1000000误认率,14-3/10000000误认率,15-3/100000000误认率,16-Automatic Normal,17-Automatic Secure,18-Automatic More Secure(目前门禁不支持) + public byte byUseLocalController; //只读,是否连接在就地控制器上,0-否,1-是 + public byte byRes1; + public short wLocalControllerID; //只读,就地控制器序号, byUseLocalController=1时有效,1-64,0代表未注册 + public short wLocalControllerReaderID; //只读,就地控制器的读卡器ID,byUseLocalController=1时有效,0代表未注册 + public short wCardReaderChannel; //只读,读卡器通信通道号,byUseLocalController=1时有效,0韦根或离线,1-RS485A,2-RS485B + public byte byFingerPrintImageQuality; //指纹图像质量,0-无效,1-低质量(V1),2-中等质量(V1),3-高质量(V1),4-最高质量(V1),5-低质量(V2),6-中等质量(V2),7-高质量(V2),8-最高质量(V2) + public byte byFingerPrintContrastTimeOut; //指纹对比超时时间,0-无效,范围1-20代表:1s-20s,0xff-无限大 + public byte byFingerPrintRecogizeInterval; //指纹连续识别间隔,0-无效,范围1-10代表:1s-10s,0xff-无延迟 + public byte byFingerPrintMatchFastMode; //指纹匹配快速模式,0-无效,范围1-5代表:快速模式1-快速模式5,0xff-自动 + public byte byFingerPrintModuleSensitive; //指纹模组灵敏度,0-无效,范围1-8代表:灵敏度级别1-灵敏度级别8 + public byte byFingerPrintModuleLightCondition; //指纹模组光线条件,0-无效,1-室外,2-室内 + public byte byFaceMatchThresholdN; //人脸比对阀值,范围0-100 + public byte byFaceQuality; //人脸质量,范围0-100 + public byte byFaceRecogizeTimeOut; //人脸识别超时时间,范围1-20代表:1s-20s,0xff-无限大 + public byte byFaceRecogizeInterval; //人脸连续识别间隔,0-无效,范围1-10代表:1s-10s,0xff-无延迟 + public short wCardReaderFunction; //只读,读卡器种类,按位表示:第1位-指纹,第二位-人脸,第三位-指静脉 + public byte[] byCardReaderDescription = new byte[CARD_READER_DESCRIPTION]; //读卡器描述 + public short wFaceImageSensitometry; //只读,人脸图像曝光度,范围0-65535 + public byte byLivingBodyDetect; //真人检测,0-无效,1-不启用,2-启用 + public byte byFaceMatchThreshold1; //人脸1:1匹配阀值,范围0-100 + public short wBuzzerTime; //蜂鸣时间,范围0s-5999s(0-代表长鸣) + public byte byFaceMatch1SecurityLevel; //人脸1:1识别安全等级,0-无效,1-一般,2-较强,3-极强 + public byte byFaceMatchNSecurityLevel; //人脸1:N识别安全等级,0-无效,1-一般,2-较强,3-极强 + public byte byEnvirMode;//人脸识别环境模式,0-无效,1-室内,2-其他; + public byte byLiveDetLevelSet;//活体检测阈值等级设置,0-无效,1-低,2-中,3-高; + public byte byLiveDetAntiAttackCntLimit;//活体检测防攻击次数, 0-无效,1-255次(客户端、设备统一次数限制,根据能力级限制); + public byte byEnableLiveDetAntiAttack;//活体检测防攻击使能,0-无效,1-不启用,2-启用 + public byte bySupportDelFPByID;//只读,读卡器是否支持按手指ID删除指纹,0-无效,1-不支持,2-支持 + public byte byFaceContrastMotionDetLevel;//人脸比对时移动侦测级别,0-无效,1-低,2-中,3-高,0xff-禁用 + public byte byDayFaceMatchThresholdN; //白天人脸1:N匹配阀值,范围0-100 + public byte byNightFaceMatchThresholdN; //夜晚人脸1:N匹配阀值,范围0-100 + public byte byFaceRecogizeEnable; //人脸识别使能:0-无效,1-开启,2-关闭 + public byte byBlackFaceMatchThreshold; //黑名单匹配阀值,范围0-100 + public byte byRes3; + public byte byDefaultVerifyMode; //只读,读卡器默认验证方式(出厂默认),1-休眠,2-刷卡+密码,3-刷卡,4-刷卡或密码,5-指纹,6-指纹+密码,7-指纹或刷卡,8-指纹+刷卡,9-指纹+刷卡+密码,10-人脸或指纹或刷卡或密码,11-人脸+指纹,12-人脸+密码,13-人脸+刷卡,14-人脸,15-工号+密码,16-指纹或密码,17-工号+指纹,18-工号+指纹+密码,19-人脸+指纹+刷卡,20-人脸+密码+指纹,21-工号+人脸,22-人脸或人脸+刷卡,23-指纹或人脸,24-刷卡或人脸或密码,25-刷卡或人脸,26-刷卡或人脸或指纹,27-刷卡或指纹或密码 + public int dwFingerPrintCapacity;//只读,指纹容量 + public int dwFingerPrintNum;//只读,已存在指纹数量 + public byte byEnableFingerPrintNum;//只读,指纹容量使能:0-不使能,1-使能(只有当该字段为1-使能时,dwFingerPrintCapacity和dwFingerPrintNum才有效) + public byte[] byRes = new byte[231]; +} + +/****************优化接口结构体定义开始*************/ +public static final int NET_DVR_GET_CARD = 2560; +public static final int NET_DVR_SET_CARD = 2561; +public static final int NET_DVR_SET_FACE = 2567; +public static final int NET_DVR_DEL_CARD = 2562; + +public static final int NET_SDK_CONFIG_STATUS_SUCCESS = 1000; +public static final int NET_SDK_CONFIG_STATUS_NEEDWAIT = 1001; +public static final int NET_SDK_CONFIG_STATUS_FINISH = 1002; +public static final int NET_SDK_CONFIG_STATUS_FAILED = 1003; +public static final int NET_SDK_CONFIG_STATUS_EXCEPTION = 1004; + +public static class NET_DVR_CARD_COND extends Structure { + public int dwSize; + public int dwCardNum; //设置或获取卡数量,获取时置为0xffffffff表示获取所有卡信息 + public byte[] byRes = new byte[64]; + } + + public static class NET_DVR_CARD_SEND_DATA extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //卡号 + public byte[] byRes = new byte[16]; + } + + public static class NET_DVR_CARD_RECORD extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; + public byte byCardType; + public byte byLeaderCard; + public byte byUserType; + public byte byRes1; + public byte[] byDoorRight = new byte[MAX_DOOR_NUM_256]; + public NET_DVR_VALID_PERIOD_CFG struValid = new NET_DVR_VALID_PERIOD_CFG(); + public byte[] byBelongGroup = new byte[MAX_GROUP_NUM_128]; + public byte[] byCardPassword= new byte[CARD_PASSWORD_LEN]; + public short[] wCardRightPlan = new short[MAX_DOOR_NUM_256]; + public int dwMaxSwipeTimes; + public int dwSwipeTimes; + public int dwEmployeeNo; + public byte[] byName = new byte[NAME_LEN]; + //按位表示,0-无权限,1-有权限 + //第0位表示:弱电报警 + //第1位表示:开门提示音 + //第2位表示:限制客卡 + //第3位表示:通道 + //第4位表示:反锁开门 + //第5位表示:巡更功能 + public int dwCardRight; + public byte[] byRes = new byte[256]; + } + + public static class NET_DVR_CARD_STATUS extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; + public int dwErrorCode; + public byte byStatus; // 状态:0-失败,1-成功 + public byte[] byRes = new byte[23]; + } + + + public static class NET_DVR_FACE_COND extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; + public int dwFaceNum; + public int dwEnableReaderNo; + public byte[] byRes = new byte[124]; + } + + public static class NET_DVR_FACE_RECORD extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; + public int dwFaceLen; + public Pointer pFaceBuffer; + public byte[] byRes = new byte[128]; + } + + public static class NET_DVR_FACE_STATUS extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; + public byte[] byErrorMsg = new byte[ERROR_MSG_LEN]; + public int dwReaderNo; + public byte byRecvStatus; + public byte[] byRes = new byte[131]; + } +/**************优化接口结构体定义结束***************************/ + +//开锁记录 +public static class NET_DVR_UNLOCK_RECORD_INFO extends Structure{ + public byte byUnlockType; //开锁方式,参考UNLOCK_TYPE_ENUM + public byte[] byRes1 = new byte[3]; //保留 + public byte[] byControlSrc = new byte[NAME_LEN]; //操作发起源信息,刷卡开锁时为卡号,蓝牙开锁时为萤石的APP账号,二维码开锁时为访客的手机号,其余情况下为设备编号 + public int dwPicDataLen; //图片数据长度 + public Pointer pImage; //图片指针 + public int dwCardUserID; //持卡人ID + public short nFloorNumber;//刷卡开锁时有效,为楼层号 + public short wRoomNumber; //操作发起源附加信息,刷卡开锁时有效,为房间号, + public short wLockID; //(对于门口机,0-表示本机控制器上接的锁、1-表示外接控制器上接的锁) + public byte[] byRes2 = new byte[2]; + public byte[] byLockName = new byte[LOCK_NAME_LEN]; //刷卡开锁时有效,锁名称,对应门参数配置中门名称 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID) + public byte[] byRes = new byte[136]; //保留 +} + +//公告信息阅读回执 +public static class NET_DVR_NOTICEDATA_RECEIPT_INFO extends Structure{ + public byte[] byNoticeNumber = new byte[MAX_NOTICE_NUMBER_LEN]; //公告编号 + public byte[] byRes = new byte[224]; //保留 +} + +//认证记录(设备未实现) +public static class NET_DVR_AUTH_INFO extends Structure{ + public byte byAuthResult; //认证结果:0-无效,1-认证成功,2-认证失败 + public byte byAuthType; //认证方式:0-无效,1-指纹,2-人脸 + public byte[] byRes1 = new byte[2]; //保留 + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN/*32*/]; //卡号 + public int dwPicDataLen; //图片数据长度(当认证方式byAuthType为人脸时有效) + public Pointer pImage; //图片指针(当认证方式byAuthType为人脸时有效) + public byte[] byRes = new byte[212]; //保留 +} + +//车牌信息上传 +public static class NET_DVR_UPLOAD_PLATE_INFO extends Structure{ + public byte[] sLicense = new byte[MAX_LICENSE_LEN]; //车牌号码 + public byte byColor; //车牌颜色,参考结构VCA_PLATE_COLOR + public byte[] byRes = new byte[239]; //保留 +} + +public static class NET_DVR_SEND_CARD_INFO extends Structure{ + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN/*32*/]; //卡号 + public byte[] byRes = new byte[224]; //保留 +} + +//可视对讲事件记录信息联合体 +public static class NET_DVR_VIDEO_INTERCOM_EVENT_INFO_UINON extends Union{ + public byte[] byLen = new byte[256]; //联合体大小 + public NET_DVR_UNLOCK_RECORD_INFO struUnlockRecord = new NET_DVR_UNLOCK_RECORD_INFO(); //开锁记录 + public NET_DVR_NOTICEDATA_RECEIPT_INFO struNoticedataReceipt = new NET_DVR_NOTICEDATA_RECEIPT_INFO(); //公告信息阅读回执 + public NET_DVR_AUTH_INFO struAuthInfo = new NET_DVR_AUTH_INFO(); //认证记录(设备未实现) + public NET_DVR_UPLOAD_PLATE_INFO struUploadPlateInfo = new NET_DVR_UPLOAD_PLATE_INFO(); //车牌信息上传 + public NET_DVR_SEND_CARD_INFO struSendCardInfo = new NET_DVR_SEND_CARD_INFO(); //门口机发卡,对应设备处于发卡状态,刷卡时上传该事件 +} + +//可视对讲事件记录 +public static class NET_DVR_VIDEO_INTERCOM_EVENT extends Structure{ + public int dwSize; //结构体大小 + public NET_DVR_TIME_EX struTime = new NET_DVR_TIME_EX(); //时间 + public byte[] byDevNumber = new byte[MAX_DEV_NUMBER_LEN]; //设备编号 + public byte byEventType; //事件信息类型,1-开锁记录,2-公告信息阅读回执,3-认证记录,4-车牌信息上传,5非法卡刷卡事件,6-门口机发卡记录(需要启动门口机发卡功能,刷卡时才会上传该事件) + public byte byPicTransType; //图片数据传输方式: 0-二进制;1-url + public byte[] byRes1 = new byte[2]; //保留 + public NET_DVR_VIDEO_INTERCOM_EVENT_INFO_UINON uEventInfo = new NET_DVR_VIDEO_INTERCOM_EVENT_INFO_UINON(); //事件信息,具体内容参考byEventType取值 + public int dwIOTChannelNo; //IOT通道号 + public byte[] byRes2 = new byte[252]; //保留 +} + +public static class NET_DVR_CONTROL_GATEWAY extends Structure{ + public int dwSize; //结构体大小 + public int dwGatewayIndex; //门禁序号,从1开始 + public byte byCommand; //操作命令,0-关闭,1-打开,2-常开(通道状态),3-恢复(普通状态) + public byte byLockType; //锁类型,0-普通(以前默认都为0),1-智能锁 + public short wLockID; //锁ID,从1开始(远程开门口机锁时,0表示门口机本机控制器上接的锁、1表示外接控制器上接的锁) + public byte[] byControlSrc = new byte[NAME_LEN]; //操作发起源信息 + public byte byControlType; //开锁类型,1-监视,2-通话 + public byte[] byRes3 = new byte[3]; + public byte[] byPassword = new byte[PASSWD_LEN]; //锁密码,当byLockType为智能锁时有效 + public byte[] byRes2 = new byte[108]; //保留 +} + + +//公告图片信息结构体 +public static class NET_DVR_NOTICE_PIC extends Structure{ + public Pointer pPicData; //图片指针 + public int dwPicDataLen; //图片数据长度 + public byte[] byRes = new byte[32]; //保留 +} + +//公告数据 +public static class NET_DVR_NOTICE_DATA extends Structure{ + public int dwSize; //结构体大小 + public NET_DVR_TIME_EX struTime = new NET_DVR_TIME_EX(); //公告时间 + public byte[] byNoticeNumber = new byte[MAX_NOTICE_NUMBER_LEN]; //公告编号 + public byte[] byNoticeTheme = new byte[MAX_NOTICE_THEME_LEN];//公告主题 + public byte[] byNoticeDetail = new byte[MAX_NOTICE_DETAIL_LEN]; //公告详情 + public byte byLevel; //公告等级,1-广告类信息;2-物业信息;3-报警类信息;4-通知类信息 + public byte byPicNum; //公告图片数量 + public byte[] byRes1 = new byte[2]; //保留 + public NET_DVR_NOTICE_PIC[] struNoticePic = new NET_DVR_NOTICE_PIC[MAX_NOTICE_PIC_NUM]; //公告图片 + public byte[] byRes2 = new byte[128]; //保留 +} + +public static class NET_DVR_DATE extends Structure{ + public short wYear; //年 + public byte byMonth; //月 + public byte byDay; //日 + + +} + +//身份证信息 +public static class NET_DVR_ID_CARD_INFO extends Structure{ + public int dwSize; //结构长度 + public byte[] byName = new byte[MAX_ID_NAME_LEN]; //姓名 + public NET_DVR_DATE struBirth; //出生日期 + public byte[] byAddr = new byte[MAX_ID_ADDR_LEN]; //住址 + public byte[] byIDNum = new byte[MAX_ID_NUM_LEN]; //身份证号码 + public byte[] byIssuingAuthority = new byte[MAX_ID_ISSUING_AUTHORITY_LEN]; //签发机关 + public NET_DVR_DATE struStartDate; //有效开始日期 + public NET_DVR_DATE struEndDate; //有效截止日期 + public byte byTermOfValidity; //是否长期有效, 0-否,1-是(有效截止日期无效) + public byte bySex; //性别,1-男,2-女 + public byte byNation; //民族,1-"汉",2-"蒙古",3-"回",4-"藏",5-"维吾尔",6-"苗",7-"彝",8-"壮",9-"布依",10-"朝鲜", + //11-"满",12-"侗",13-"瑶",14-"白",15-"土家",16-"哈尼",17-"哈萨克",18-"傣",19-"黎",20-"傈僳", + //21-"佤",22-"畲",23-"高山",24-"拉祜",25-"水",26-"东乡",27-"纳西",28-"景颇",29-"柯尔克孜",30-"土", + //31-"达斡尔",32-"仫佬",33-"羌",34-"布朗",35-"撒拉",36-"毛南",37-"仡佬",38-"锡伯",39-"阿昌",40-"普米", + //41-"塔吉克",42-"怒",43-"乌孜别克",44-"俄罗斯",45-"鄂温克",46-"德昂",47-"保安",48-"裕固",49-"京",50-"塔塔尔", + //51-"独龙",52-"鄂伦春",53-"赫哲",54-"门巴",55-"珞巴",56-"基诺" + public byte[] byRes = new byte[101]; + + +} + +public static class NET_DVR_ACS_EVENT_INFO_EXTEND_V20 extends Structure{ + public byte byRemoteCheck; //是否需要远程核验(0-无效,1-不需要(默认),2-需要) + public byte byThermometryUnit; //测温单位(0-摄氏度(默认),1-华氏度,2-开尔文) + public byte byIsAbnomalTemperature; //人脸抓拍测温是否温度异常:1-是,0-否 + public byte byRes2; + public float fCurrTemperature; //人脸温度(精确到小数点后一位) + public NET_VCA_POINT struRegionCoordinates = new NET_VCA_POINT(); //人脸温度坐标 + public int dwQRCodeInfoLen; //二维码信息长度,不为0是表示后面带数据 + public int dwVisibleLightDataLen; //热成像相机可见光图片长度,不为0是表示后面带数据 + public int dwThermalDataLen; //热成像图片长度,不为0是表示后面带数据 + public Pointer pQRCodeInfo; //二维码信息指针 + public Pointer pVisibleLightData; //热成像相机可见光图片指针 + public Pointer pThermalData; //热成像图片指针 + public byte[] byRes = new byte[1024]; + + +} + +//门禁主机报警信息结构体 +public static class NET_DVR_ACS_ALARM_INFO extends Structure{ + public int dwSize; + public int dwMajor; //报警主类型,参考宏定义 + public int dwMinor; //报警次类型,参考宏定义 + public NET_DVR_TIME struTime = new NET_DVR_TIME(); //时间 + public byte[] sNetUser = new byte[MAX_NAMELEN] ;//网络操作的用户名 + public NET_DVR_IPADDR struRemoteHostAddr = new NET_DVR_IPADDR();//远程主机地址 + public NET_DVR_ACS_EVENT_INFO struAcsEventInfo = new NET_DVR_ACS_EVENT_INFO(); //详细参数 + public int dwPicDataLen; //图片数据大小,不为0是表示后面带数据 + public Pointer pPicData; + public short wInductiveEventType; //归纳事件类型,0-无效,客户端判断该值为非0值后,报警类型通过归纳事件类型区分,否则通过原有报警主次类型(dwMajor、dwMinor)区分 + public byte byPicTransType; //图片数据传输方式: 0-二进制;1-url + public byte byRes1; //保留字节 + public int dwIOTChannelNo; //IOT通道号 + public Pointer pAcsEventInfoExtend; //byAcsEventInfoExtend为1时,表示指向一个NET_DVR_ACS_EVENT_INFO_EXTEND结构体 + public byte byAcsEventInfoExtend; //pAcsEventInfoExtend是否有效:0-无效,1-有效 + public byte byTimeType; //时间类型:0-设备本地时间,1-UTC时间(struTime的时间) + public byte byRes2; //保留字节 + public byte byAcsEventInfoExtendV20; //pAcsEventInfoExtendV20是否有效:0-无效,1-有效 + public Pointer pAcsEventInfoExtendV20; //byAcsEventInfoExtendV20为1时,表示指向一个NET_DVR_ACS_EVENT_INFO_EXTEND_V20结构体 + public byte[] byRes = new byte[4]; + + +} + +//门禁主机事件信息 +public static class NET_DVR_ACS_EVENT_INFO extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[32]; + public byte byCardType; + public byte byWhiteListNo; + public byte byReportChannel; + public byte byCardReaderKind; + public int dwCardReaderNo; + public int dwDoorNo; + public int dwVerifyNo; + public int dwAlarmInNo; + public int dwAlarmOutNo; + public int dwCaseSensorNo; + public int dwRs485No; + public int dwMultiCardGroupNo; + public short wAccessChannel; + public byte byDeviceNo; + public byte byDistractControlNo; + public int dwEmployeeNo; + public short wLocalControllerID; + public byte byInternetAccess; + public byte byType; + public byte[] byMACAddr = new byte[MACADDR_LEN]; //物理地址,为0无效 + public byte bySwipeCardType;//刷卡类型,0-无效,1-二维码 + public byte byRes2; + public int dwSerialNo; //事件流水号,为0无效 + public byte byChannelControllerID; //通道控制器ID,为0无效,1-主通道控制器,2-从通道控制器 + public byte byChannelControllerLampID; //通道控制器灯板ID,为0无效(有效范围1-255) + public byte byChannelControllerIRAdaptorID; //通道控制器红外转接板ID,为0无效(有效范围1-255) + public byte byChannelControllerIREmitterID; //通道控制器红外对射ID,为0无效(有效范围1-255) + public byte[] byRes = new byte[4]; + + +} + +public static final int NET_DEV_NAME_LEN = 64; +public static class NET_DVR_ACS_EVENT_INFO_EXTEND extends Structure { + public int dwFrontSerialNo; //事件流水号,为0无效(若该字段为0,平台根据dwSerialNo判断是否丢失事件;若该字段不为0,平台根据该字段和dwSerialNo字段共同判断是否丢失事件)(主要用于解决报警订阅后导致dwSerialNo不连续的情况) + public byte byUserType; //人员类型:0-无效,1-普通人(主人),2-来宾(访客),3-黑名单人,4-管理员 + public byte byCurrentVerifyMode; //读卡器当前验证方式:0-无效,1-休眠,2-刷卡+密码,3-刷卡,4-刷卡或密码,5-指纹,6-指纹+密码,7-指纹或刷卡,8-指纹+刷卡,9-指纹+刷卡+密码,10-人脸或指纹或刷卡或密码,11-人脸+指纹,12-人脸+密码,13-人脸+刷卡,14-人脸,15-工号+密码,16-指纹或密码,17-工号+指纹,18-工号+指纹+密码,19-人脸+指纹+刷卡,20-人脸+密码+指纹,21-工号+人脸,22-人脸或人脸+刷卡,23-指纹或人脸,24-刷卡或人脸或密码,25-刷卡或人脸,26-刷卡或人脸或指纹,27-刷卡或指纹或密码 + public byte byCurrentEvent; //是否为实时事件:0-无效,1-是(实时事件),2-否(离线事件) + public byte byPurePwdVerifyEnable; //设备是否支持纯密码认证, 0-不支持,1-支持 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID)(对于设备来说,如果使用了工号(人员ID)字段,byEmployeeNo一定要传递,如果byEmployeeNo可转换为dwEmployeeNo,那么该字段也要传递;对于上层平台或客户端来说,优先解析byEmployeeNo字段,如该字段为空,再考虑解析dwEmployeeNo字段) + public byte byAttendanceStatus; //考勤状态:0-未定义,1-上班,2-下班,3-开始休息,4-结束休息,5-开始加班,6-结束加班 + public byte byStatusValue; //考勤状态值 + public byte[] byRes2 = new byte[2]; + public byte[] byUUID = new byte[NET_SDK_UUID_LEN/*36*/]; //UUID(该字段仅在对接萤石平台过程中才会使用) + public byte[] byDeviceName = new byte[NET_DEV_NAME_LEN/*64*/]; //设备序列号 + public byte[] byRes = new byte[24]; + +} + +public static class NET_DVR_ACS_EVENT_DETAIL extends Structure { + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //卡号(mac地址),为0无效 + public byte byCardType; //卡类型,1-普通卡,2-残疾人卡,3-黑名单卡,4-巡更卡,5-胁迫卡,6-超级卡,7-来宾卡,8-解除卡,为0无效 + public byte byWhiteListNo; //白名单单号,1-8,为0无效 + public byte byReportChannel; //报告上传通道,1-布防上传,2-中心组1上传,3-中心组2上传,为0无效 + public byte byCardReaderKind; //读卡器属于哪一类,0-无效,1-IC读卡器,2-身份证读卡器,3-二维码读卡器,4-指纹头 + public int dwCardReaderNo; //读卡器编号,为0无效 + public int dwDoorNo; //门编号(楼层编号),为0无效 + public int dwVerifyNo; //多重卡认证序号,为0无效 + public int dwAlarmInNo; //报警输入号,为0无效 + public int dwAlarmOutNo; //报警输出号,为0无效 + public int dwCaseSensorNo; //事件触发器编号 + public int dwRs485No; //RS485通道号,为0无效 + public int dwMultiCardGroupNo; //群组编号 + public short wAccessChannel; //人员通道号 + public byte byDeviceNo; //设备编号,为0无效(有效范围1-255) + public byte byDistractControlNo;//分控器编号,为0无效 + public int dwEmployeeNo; //工号,为0无效 + public short wLocalControllerID; //就地控制器编号,0-门禁主机,1-64代表就地控制器 + public byte byInternetAccess; //网口ID:(1-上行网口1,2-上行网口2,3-下行网口1) + public byte byType; //防区类型,0:即时防区,1-24小时防区,2-延时防区 ,3-内部防区,4-钥匙防区 5-火警防区 6-周界防区 7-24小时无声防区 8-24小时辅助防区,9-24小时震动防区,10-门禁紧急开门防区,11-门禁紧急关门防区 0xff-无 + public byte[] byMACAddr = new byte[MACADDR_LEN]; //物理地址,为0无效 + public byte bySwipeCardType;//刷卡类型,0-无效,1-二维码 + public byte byEventAttribute; //事件属性:0-未定义,1-合法认证,2-其它 + public int dwSerialNo; //事件流水号,为0无效 + public byte byChannelControllerID; //通道控制器ID,为0无效,1-主通道控制器,2-从通道控制器 + public byte byChannelControllerLampID; //通道控制器灯板ID,为0无效(有效范围1-255) + public byte byChannelControllerIRAdaptorID; //通道控制器红外转接板ID,为0无效(有效范围1-255) + public byte byChannelControllerIREmitterID; //通道控制器红外对射ID,为0无效(有效范围1-255) + public int dwRecordChannelNum; //录像通道数目 + public Pointer pRecordChannelData;//录像通道,大小为sizeof(DWORD)* dwRecordChannelNum + public byte byUserType; //人员类型:0-无效,1-普通人(主人),2-来宾(访客),3-黑名单人,4-管理员 + public byte byCurrentVerifyMode; //读卡器当前验证方式:0-无效,1-休眠,2-刷卡+密码,3-刷卡,4-刷卡或密码,5-指纹,6-指纹+密码,7-指纹或刷卡,8-指纹+刷卡,9-指纹+刷卡+密码,10-人脸或指纹或刷卡或密码,11-人脸+指纹,12-人脸+密码, + //13-人脸+刷卡,14-人脸,15-工号+密码,16-指纹或密码,17-工号+指纹,18-工号+指纹+密码,19-人脸+指纹+刷卡,20-人脸+密码+指纹,21-工号+人脸,22-人脸或人脸+刷卡,23-指纹或人脸,24-刷卡或人脸或密码,25-刷卡或人脸,26-刷卡或人脸或指纹,27-刷卡或指纹或密码 + public byte byAttendanceStatus; //考勤状态:0-未定义,1-上班,2-下班,3-开始休息,4-结束休息,5-开始加班,6-结束加班 + public byte byStatusValue; //考勤状态值 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID)(对于设备来说,如果使用了工号(人员ID)字段,byEmployeeNo一定要传递,如果byEmployeeNo可转换为dwEmployeeNo,那么该字段也要传递;对于上层平台或客户端来说,优先解析byEmployeeNo字段,如该字段为空,再考虑解析dwEmployeeNo字段) + public byte[] byRes = new byte[64]; +} + +public static class NET_DVR_ACS_EVENT_CFG extends Structure { + public int dwSize; + public int dwMajor; //报警主类型,参考宏定义 + public int dwMinor; //报警次类型,参考宏定义 + public NET_DVR_TIME struTime; //时间 + public byte[] sNetUser = new byte[MAX_NAMELEN];//网络操作的用户名 + public NET_DVR_IPADDR struRemoteHostAddr;//远程主机地址 + public NET_DVR_ACS_EVENT_DETAIL struAcsEventInfo; //详细参数 + public int dwPicDataLen; //图片数据大小,不为0是表示后面带数据 + public Pointer pPicData; + public short wInductiveEventType; //归纳事件类型,0-无效,其他值参见2.2章节,客户端判断该值为非0值后,报警类型通过归纳事件类型区分,否则通过原有报警主次类型(dwMajor、dwMinor)区分 + public byte byTimeType; //时间类型:0-设备本地时间(默认),1-UTC时间(struTime的时间) + public byte[] byRes = new byte[61]; +} + +public static final int NET_SDK_MONITOR_ID_LEN = 64; +public static class NET_DVR_ACS_EVENT_COND extends Structure { + public int dwSize; + public int dwMajor; //报警主类型,参考事件上传宏定义,0-全部 + public int dwMinor; //报警次类型,参考事件上传宏定义,0-全部 + public NET_DVR_TIME struStartTime; //开始时间 + public NET_DVR_TIME struEndTime; //结束时间 + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //卡号 + public byte[] byName = new byte[NAME_LEN]; //持卡人姓名 + public byte byPicEnable; //是否带图片,0-不带图片,1-带图片 + public byte byTimeType; //时间类型:0-设备本地时间(默认),1-UTC时间(struStartTime和struEndTime的时间) + public byte[] byRes2 = new byte[2]; //保留 + public int dwBeginSerialNo; //起始流水号(为0时默认全部) + public int dwEndSerialNo; //结束流水号(为0时默认全部) + public int dwIOTChannelNo; //IOT通道号,0-无效 + public short wInductiveEventType; //归纳事件类型,0-无效,其他值参见2.2章节,客户端判断该值为非0值后,报警类型通过归纳事件类型区分,否则通过原有报警主次类型(dwMajor、dwMinor)区分 + public byte bySearchType; //搜索方式:0-保留,1-按事件源搜索(此时通道号为非视频通道号),2-按监控点ID搜索 + public byte byEventAttribute; //事件属性:0-未定义,1-合法事件,2-其它 + public byte[] szMonitorID = new byte[NET_SDK_MONITOR_ID_LEN/*64*/]; //监控点ID(由设备序列号、通道类型、编号组成,例如门禁点:设备序列号+“DOOR”+门编号) + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID) + public byte[] byRes = new byte[140]; //保留 +} + +public static class NET_DVR_FACE_PARAM_COND extends Structure +{ + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //人脸关联的卡号 + public byte[] byEnableCardReader = new byte[MAX_CARD_READER_NUM_512]; //人脸的读卡器是否有效,0-无效,1-有效 + public int dwFaceNum; //设置或获取人脸数量,获取时置为0xffffffff表示获取所有人脸信息 + public byte byFaceID; //人脸编号,有效值范围为1-2 0xff表示该卡所有人脸 + public byte[] byRes = new byte[127]; //保留 +} + +public static class NET_DVR_FACE_PARAM_CFG extends Structure +{ + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //人脸关联的卡号 + public int dwFaceLen; //人脸数据长度,设备端返回的即加密后的数据 + public Pointer pFaceBuffer; //人脸数据指针 + public byte[] byEnableCardReader = new byte[MAX_CARD_READER_NUM_512]; //需要下发人脸的读卡器,按数组表示,从低位到高位表示,0-不下发该读卡器,1-下发到该读卡器 + public byte byFaceID; //人脸编号,有效值范围为1-2 + public byte byFaceDataType; //人脸数据类型:0-模板(默认),1-图片 + public byte[] byRes = new byte[126]; +} + +public static class NET_DVR_FACE_PARAM_STATUS extends Structure +{ + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //人脸关联的卡号 + public byte[] byCardReaderRecvStatus = new byte[MAX_CARD_READER_NUM_512]; //人脸读卡器状态,按字节表示,0-失败,1-成功,2-重试或人脸质量差,3-内存已满,4-已存在该人脸,5-非法人脸ID + public byte[] byErrorMsg = new byte[ERROR_MSG_LEN]; //下发错误信息,当byCardReaderRecvStatus为4时,表示已存在人脸对应的卡号 + public int dwCardReaderNo; //纹读卡器编号,可用于下发错误返回 + public byte byTotalStatus; //下发总的状态,0-当前人脸未下完所有读卡器,1-已下完所有读卡器(这里的所有指的是门禁主机往所有的读卡器下发了,不管成功与否) + public byte byFaceID; //人脸编号,有效值范围为1-2 + public byte[] byRes = new byte[130]; +} + +public static class NET_DVR_FACE_PARAM_BYCARD extends Structure +{ + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //人脸关联的卡号 + public byte[] byEnableCardReader = new byte[MAX_CARD_READER_NUM_512]; //人脸的读卡器信息,按数组表示 + public byte[] byFaceID = new byte[MAX_FACE_NUM]; //需要删除的人脸编号,按数组下标,值表示0-不删除,1-删除该人脸 + public byte[] byRes1 = new byte[42]; //保留 +} + +public static class NET_DVR_FACE_PARAM_BYREADER extends Structure +{ + public int dwCardReaderNo; //按值表示,人脸读卡器编号 + public byte byClearAllCard; //是否删除所有卡的人脸信息,0-按卡号删除人脸信息,1-删除所有卡的人脸信息 + public byte[] byRes1 = new byte[3]; //保留 + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //人脸关联的卡号 + public byte[] byRes = new byte[548]; //保留 +} + + public static class NET_DVR_DEL_FACE_PARAM_MODE extends Union +{ + public byte[] uLen = new byte[588]; //联合体长度 + public NET_DVR_FACE_PARAM_BYCARD struByCard; //按卡号的方式删除 + public NET_DVR_FACE_PARAM_BYREADER struByReader; //按读卡器的方式删除 +} + +public static class NET_DVR_FACE_PARAM_CTRL extends Structure +{ + public int dwSize; + public byte byMode; //删除方式,0-按卡号方式删除,1-按读卡器删除 + public byte[] byRes1 = new byte[3]; //保留 + public NET_DVR_DEL_FACE_PARAM_MODE struProcessMode; //处理方式 + public byte[] byRes = new byte[64]; //保留 +} + +public static class NET_DVR_FINGER_PRINT_CFG_V50 extends Structure +{ + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //指纹关联的卡号 + public int dwFingerPrintLen; //指纹数据长度 + public byte[] byEnableCardReader = new byte[MAX_CARD_READER_NUM_512]; //需要下发指纹的读卡器,按数组表示,从低位到高位表示,0-不下发该读卡器,1-下发到该读卡器 + public byte byFingerPrintID; //手指编号,有效值范围为1-10 + public byte byFingerType; //指纹类型 0-普通指纹,1-胁迫指纹,2-巡更指纹,3-超级指纹,4-解除指纹 + public byte[] byRes1 = new byte[30]; + public byte[] byFingerData = new byte[MAX_FINGER_PRINT_LEN]; //指纹数据内容 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID) + public byte[] byLeaderFP = new byte[MAX_DOOR_NUM_256]; //对门是否有首次认证功能(按字节表示):0-无首次认证功能,1-有首次认证功能 + public byte[] byRes = new byte[128]; +} + +public static class NET_DVR_FINGER_PRINT_STATUS_V50 extends Structure +{ + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //指纹关联的卡号 + public byte[] byCardReaderRecvStatus = new byte[MAX_CARD_READER_NUM_512]; //指纹读卡器状态,按字节表示,0-失败,1-成功,2-该指纹模组不在线,3-重试或指纹质量差,4-内存已满,5-已存在该指纹,6-已存在该指纹ID,7-非法指纹ID,8-该指纹模组无需配置,10-指纹读卡器版本过低(无法支持工号) + public byte byFingerPrintID; //手指编号,有效值范围为1-10 + public byte byFingerType; //指纹类型 0-普通指纹,1-胁迫指纹,2-巡更指纹,3-超级指纹,4-解除指纹 + public byte byTotalStatus; //下发总的状态,0-当前指纹未下完所有读卡器,1-已下完所有读卡器(这里的所有指的是门禁主机往所有的读卡器下发了,不管成功与否) + public byte byRecvStatus; //主机错误状态:0-成功,1-手指编号错误,2-指纹类型错误,3-卡号错误(卡号规格不符合设备要求),4-指纹未关联工号或卡号(工号或卡号字段为空),5-工号不存在,6-指纹数据长度为0,7-读卡器编号错误,8-工号错误 + public byte[] byErrorMsg = new byte[ERROR_MSG_LEN]; //下发错误信息,当byCardReaderRecvStatus为5时,表示已存在指纹对应的卡号 + public int dwCardReaderNo; //当byCardReaderRecvStatus为5时,表示已存在指纹对应的指纹读卡器编号,可用于下发错误返回。0时表示无错误信息 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID) + public byte[] byErrorEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //下发错误信息,当byCardReaderRecvStatus为5时,表示已存在指纹对应的工号(人员ID) + public byte[] byRes = new byte[128]; +} + +public static class NET_DVR_FINGER_PRINT_INFO_COND_V50 extends Structure +{ + public int dwSize; + public byte[] byCardNo = new byte[ACS_CARD_NO_LEN]; //指纹关联的卡号(该字段获取时有效,设置时无效) + public byte[] byEnableCardReader = new byte[MAX_CARD_READER_NUM_512]; //指纹的读卡器是否有效,0-无效,1-有效 + public int dwFingerPrintNum; //设置或获指纹数量,获取时置为0xffffffff表示获取所有指纹信息 + public byte byFingerPrintID; //手指编号,有效值范围为1-10 0xff表示该卡所有指纹 + public byte byCallbackMode; //设备回调方式,0-设备所有读卡器下完了返回,1-在时间段内下了部分也返回 + public byte[] byRes2 = new byte[2]; //保留 + public byte[] byEmployeeNo = new byte[NET_SDK_EMPLOYEE_NO_LEN]; //工号(人员ID) + public byte[] byRes1 = new byte[128]; //保留 +} + +//自定义结构体,用于二维数组转换 +public static class NET_DVR_SINGLE_PLAN_SEGMENT_WEEK extends Structure +{ + public NET_DVR_SINGLE_PLAN_SEGMENT[] struPlanCfgDay = new NET_DVR_SINGLE_PLAN_SEGMENT[MAX_TIMESEGMENT_V30]; //一天的计划参数 +} + +public static class NET_DVR_WEEK_PLAN_CFG extends Structure +{ + public int dwSize; + public byte byEnable; //是否使能,1-使能,0-不使能 + public byte[] byRes1 = new byte[3]; + public NET_DVR_SINGLE_PLAN_SEGMENT_WEEK[] struPlanCfg = new NET_DVR_SINGLE_PLAN_SEGMENT_WEEK[MAX_DAYS]; //周计划参数 + public byte[] byRes2 = new byte[16]; +} + +public static class NET_DVR_SINGLE_PLAN_SEGMENT extends Structure +{ + public byte byEnable; //是否使能,1-使能,0-不使能 + public byte byDoorStatus; //门状态模式(梯控模式),0-无效,1-常开状态(自由),2-常闭状态(禁用),3-普通状态(门状态计划使用) + public byte byVerifyMode; //验证方式,0-无效,1-刷卡,2-刷卡+密码(读卡器验证方式计划使用),3-刷卡,4-刷卡或密码(读卡器验证方式计划使用), 5-指纹,6-指纹+密码,7-指纹或刷卡,8-指纹+刷卡,9-指纹+刷卡+密码(无先后顺序),10-人脸或指纹或刷卡或密码,11-人脸+指纹,12-人脸+密码, + //13-人脸+刷卡,14-人脸,15-工号+密码,16-指纹或密码,17-工号+指纹,18-工号+指纹+密码,19-人脸+指纹+刷卡,20-人脸+密码+指纹,21-工号+人脸,22-人脸或人脸+刷卡 + public byte[] byRes = new byte[5]; + public NET_DVR_TIME_SEGMENT struTimeSegment; //时间段参数 +} + + +public static class NET_DVR_TIME_SEGMENT extends Structure +{ + public NET_DVR_SIMPLE_DAYTIME struBeginTime; //开始时间点 + public NET_DVR_SIMPLE_DAYTIME struEndTime; //结束时间点 +} + +public static class NET_DVR_SIMPLE_DAYTIME extends Structure +{ + public byte byHour; //时 + public byte byMinute; //分 + public byte bySecond; //秒 + public byte byRes; +} + +public static class NET_DVR_WEEK_PLAN_COND extends Structure +{ + public int dwSize; + public int dwWeekPlanNumber; //周计划编号 + public short wLocalControllerID; //就地控制器序号[1,64] + public byte[] byRes = new byte[106]; +} + +public static final int TEMPLATE_NAME_LEN = 32; //计划模板名称长度 +public static final int MAX_HOLIDAY_GROUP_NUM = 16; //计划模板最大假日组数 + +public static class NET_DVR_PLAN_TEMPLATE extends Structure +{ + public int dwSize; + public byte byEnable; //是否启用,1-启用,0-不启用 + public byte[] byRes1 = new byte[3]; + public byte[] byTemplateName = new byte[TEMPLATE_NAME_LEN]; //模板名称 + public int dwWeekPlanNo; //周计划编号,0为无效 + public int[] dwHolidayGroupNo = new int[MAX_HOLIDAY_GROUP_NUM]; //假日组编号,就前填充,遇0无效 + public byte[] byRes2 = new byte[32]; +} + +public static class NET_DVR_PLAN_TEMPLATE_COND extends Structure +{ + public int dwSize; + public int dwPlanTemplateNumber; //计划模板编号,从1开始,最大值从门禁能力集获取 + public short wLocalControllerID; //就地控制器序号[1,64],0无效 + public byte[] byRes = new byte[106]; +} + +public static class NET_DVR_CAPTURE_FACE_COND extends Structure +{ + public int dwSize; + public byte[] byRes = new byte[128]; +} + +public static class NET_DVR_CAPTURE_FACE_CFG extends Structure +{ + public int dwSize; + public int dwFaceTemplate1Size; //人脸模板1数据大小,等于0时,代表无人脸模板1数据 + public Pointer pFaceTemplate1Buffer; //人脸模板1数据缓存(不大于2.5k) + public int dwFaceTemplate2Size; //人脸模板2数据大小,等于0时,代表无人脸模板2数据 + public Pointer pFaceTemplate2Buffer; //人脸模板2数据缓存(不大于2.5K) + public int dwFacePicSize; //人脸图片数据大小,等于0时,代表无人脸图片数据 + public Pointer pFacePicBuffer; //人脸图片数据缓存 + public byte byFaceQuality1; //人脸质量,范围1-100 + public byte byFaceQuality2; //人脸质量,范围1-100 + public byte byCaptureProgress; //采集进度,目前只有两种进度值:0-未采集到人脸,100-采集到人脸(只有在进度为100时,才解析人脸信息) + public byte[] byRes = new byte[125]; +} + +public static class NET_DVR_XML_CONFIG_INPUT extends Structure { + public int dwSize; + public Pointer lpRequestUrl; + public int dwRequestUrlLen; + public Pointer lpInBuffer; + public int dwInBufferSize; + public int dwRecvTimeOut; + public byte[] byRes = new byte[32]; +} + +public static class NET_DVR_STRING_POINTER extends Structure { + public byte[] byString = new byte[2*1024]; +} + +public static class NET_DVR_XML_CONFIG_OUTPUT extends Structure { + public int dwSize; + public Pointer lpOutBuffer; + public int dwOutBufferSize; + public int dwReturnedXMLSize; + public Pointer lpStatusBuffer; + public int dwStatusSize; + public byte[] byRes = new byte[32]; +} + +//报警场景信息 +public static class NET_DVR_SCENE_INFO extends Structure { + public int dwSceneID; //场景ID, 0 - 表示该场景无效 + public byte[] bySceneName = new byte[NAME_LEN]; //场景名称 + public byte byDirection; //监测方向 1-上行,2-下行,3-双向,4-由东向西,5-由南向北,6-由西向东,7-由北向南,8-其它 + public byte[] byRes1 = new byte[3]; //保留 + public NET_DVR_PTZPOS struPtzPos; //Ptz 坐标 + public byte[] byRes2 = new byte[64] ; //保留 +} + +// 方向结构体 +public static class NET_DVR_DIRECTION extends Structure { + public NET_VCA_POINT struStartPoint = new NET_VCA_POINT(); // 方向起始点 + public NET_VCA_POINT struEndPoint = new NET_VCA_POINT(); // 方向结束点 +} + +// 交通事件信息 +public static class NET_DVR_AID_INFO extends Structure { + public byte byRuleID; // 规则序号,为规则配置结构下标,0-16 + public byte[] byRes1 = new byte[3]; + public byte[] byRuleName = new byte[NAME_LEN]; // 规则名称 + public int dwAIDType; // 报警事件类型 + public NET_DVR_DIRECTION struDirect = new NET_DVR_DIRECTION(); // 报警指向区域 + public byte bySpeedLimit; //限速值,单位km/h[0,255] + public byte byCurrentSpeed; //当前速度值,单位km/h[0,255] + public byte byVehicleEnterState; //车辆出入状态:0- 无效,1- 驶入,2- 驶出 + public byte byState; //0-变化上传,1-轮巡上传 + public byte[] byParkingID = new byte[16]; //停车位编号 + public byte[] byRes2 = new byte[20]; // 保留字节 +} + +public int ILLEGAL_LEN = 32; //违法代码长度 +public int MONITORSITE_ID_LEN = 48;//监测点编号长度 +public int DEVICE_ID_LEN = 48; + +//交通取证报警 +public static class NET_DVR_TFS_ALARM extends Structure { + public int dwSize; //结构体大小 + public int dwRelativeTime; //相对时标 + public int dwAbsTime; //绝对时标 + public int dwIllegalType; //违章类型,采用国标定义,当dwIllegalType值为0xffffffff时使用byIllegalCode + public int dwIllegalDuration; //违法持续时间(单位:秒) = 抓拍最后一张图片的时间 - 抓拍第一张图片的时间 + public byte[] byMonitoringSiteID = new byte[MONITORSITE_ID_LEN];//监测点编号(路口编号、内部编号) + public byte[] byDeviceID = new byte[DEVICE_ID_LEN]; //设备编号 + public NET_VCA_DEV_INFO struDevInfo = new NET_VCA_DEV_INFO(); //前端设备信息 + public NET_DVR_SCENE_INFO struSceneInfo = new NET_DVR_SCENE_INFO(); //场景信息 + public NET_DVR_TIME_EX struBeginRecTime = new NET_DVR_TIME_EX(); //录像开始时间 + public NET_DVR_TIME_EX struEndRecTime = new NET_DVR_TIME_EX(); //录像结束时间 + public NET_DVR_AID_INFO struAIDInfo = new NET_DVR_AID_INFO(); //交通事件信息 + public NET_DVR_PLATE_INFO struPlateInfo = new NET_DVR_PLATE_INFO(); //车牌信息 + public NET_DVR_VEHICLE_INFO struVehicleInfo = new NET_DVR_VEHICLE_INFO(); //车辆信息 + public int dwPicNum; //图片数量 + public NET_ITS_PICTURE_INFO[] struPicInfo = new NET_ITS_PICTURE_INFO[8]; //图片信息,最多8张 + public byte bySpecificVehicleType; //具体车辆种类 参考识别结果类型VTR_RESULT + public byte byLaneNo; //关联车道号 + public byte[] byRes1 = new byte[2]; //保留 + public NET_DVR_TIME_V30 struTime = new NET_DVR_TIME_V30();//手动跟踪定位,当前时间。 + public int dwSerialNo;//序号; + public byte byVehicleAttribute;//车辆属性,按位表示,0- 无附加属性(普通车),bit1- 黄标车(类似年检的标志),bit2- 危险品车辆,值:0- 否,1- 是 + public byte byPilotSafebelt;//0-表示未知,1-系安全带,2-不系安全带 + public byte byCopilotSafebelt;//0-表示未知,1-系安全带,2-不系安全带 + public byte byPilotSunVisor;//0-表示未知,1-不打开遮阳板,2-打开遮阳板 + public byte byCopilotSunVisor;//0-表示未知, 1-不打开遮阳板,2-打开遮阳板 + public byte byPilotCall;// 0-表示未知, 1-不打电话,2-打电话 + public byte[] byRes2 = new byte[2]; //保留 + public byte[] byIllegalCode = new byte[ILLEGAL_LEN/*32*/];//违法代码扩展,当dwIllegalType值为0xffffffff;使用这个值 + public short wCountry; // 国家索引值,参照枚举COUNTRY_INDEX + public byte byRegion; //区域索引值,0-保留,1-欧洲(Europe Region),2-俄语区域(Russian Region),3-欧洲&俄罗斯(EU&CIS) , 4-中东(Middle East),0xff-所有 + public byte byCrossLine;//是否压线停车(侧方停车),0-表示未知,1-不压线,2-压线 + public byte[] byParkingSerialNO = new byte[16];//泊车位编号 + public byte byCrossSpaces;//是否跨泊车位停车(侧方停车),0-表示未知,1-未跨泊车位停车,2-跨泊车位停车 + public byte byAngledParking;//是否倾斜停车(侧方停车), 0-表示未知,1-未倾斜停车,2-倾斜停车 + public byte byAlarmValidity;//报警置信度,可以输出驶入驶出的置信度,范围0-100;置信度越高,事件真实性越高 + public byte byDoorsStatus;//车门状态 0-车门关闭 1-车门开启 + public int dwXmlLen;//XML报警信息长度 + public Pointer pXmlBuf; // XML报警信息指针,其XML对应到EventNotificationAlert XML Block + public byte byVehicleHeadTailStatus;//车头车尾状态 0-保留 1-车头 2-车尾 + public byte[] byRes = new byte[31]; //保留 +} + +public static class NET_ITS_OVERLAPCFG_COND extends Structure{ + public int dwSize; + public int dwChannel; + public int dwConfigMode; //配置模式,0-终端,1-前端(直连前端或终端接前端) + public byte byPicModeType;//0-表示小图(独立图),1-表示大图(合成图) + /* + 0表示关联 抓拍MPR模式(多帧触发抓拍 IPC使用) + 1 表示关联 抓拍 HVT 模式(混卡IPC使用) + */ + public byte byRelateType; + public byte[] byRes = new byte[14]; + + } + + //字符叠加每一条信息结构体 + public static class NET_ITS_OVERLAP_SINGLE_ITEM_PARAM_V50 extends Structure{ + public byte[] byRes1 = new byte[2]; // 保留 + public byte byItemType; //类型,详见OVERLAP_ITEM_TYPE + public byte byChangeLineNum; //叠加项后的换行数[0-10](默认0) + public byte bySpaceNum; //叠加项后的空格数[0-255](默认0) + public byte[] byRes2 = new byte[2]; + public byte byEnablePos; //是否启用坐标显示 + public short wStartPosTop; //起始上坐标,只对图片内部叠加有效[0~2448](默认0) + public short wStartPosLeft; //起始左坐标,只对图片内部叠加有效[0~2448](默认0) + //自定义类型;与byItemType参数对应。可将byItemType参数类型自定义名称。若自定义内容为空,便默认以byItemType参数中的类型命名。 + public byte[] byItemTypeCustom = new byte[32]; + public byte[] byRes = new byte[8]; + + } + + public int MAX_OVERLAP_ITEM_NUM = 50; //最大字符叠加种数 + public static class NET_ITS_OVERLAP_ITEM_PARAM_V50 extends Structure{ + public NET_ITS_OVERLAP_SINGLE_ITEM_PARAM_V50[] struSingleItem = new NET_ITS_OVERLAP_SINGLE_ITEM_PARAM_V50[MAX_OVERLAP_ITEM_NUM]; //单条字符参数 + public int dwLinePercent; //叠加行百分比(0-100),(默认100) + public int dwItemsStlye; //叠加方式:0-横排,1-竖排(默认横排) + public short wStartPosTop; //起始上坐标,只对图片内部叠加有效[0~2448](默认0) + public short wStartPosLeft; //起始左坐标,只对图片内部叠加有效[0~2448](默认0) + public short wCharStyle; //字体类型,0-宋体1-魏体(默认) + public short wCharSize; //字符大小,0--16x16,1--32x32,2-48x48,3--64x64 (默认),8x128(Ver3.7) + public short wCharInterval; //字符间距,[0~16],可设单位:像素(默认) + public byte[] byRes1 = new byte[2]; + public int dwForeClorRGB; //前景色的RGB值bit0-1:(B) bit2-3:(G) bit4-5:(G) (默认x00FFFFFF-白) + public int dwBackClorRGB; //背景色的RGB值,只对图片外叠加有效bit0-1:(B) bit2-3:(G) bit4-5:(G) (默认x00000000-黑) + public byte byColorAdapt; //颜色是否自适应0-否1-是 + //(Ver3.7 新增) + // 参数补零使能 0-补零, 1-不补零(详细注释)速度,限速值 不足3位补0 + public byte byParamFillZeroEnble; + public byte byPlateLeftCornerEnable;// 车牌小图叠加左上角使能 0-不叠加, 1-叠加 + public byte byRes2; + public short wStartSPicPosTop; //起始上坐标,只对图片内部叠加有效[0~2448](默认0) + public short wStartSPicPosLeft; //起始左坐标,只对图片内部叠加有效[0~2448](默认0) + //OSD叠加位置 0-图片内,1-图片上边缘,2-图片下边缘(合成图专用的是上边缘外)(V3.7) + public byte byOsdLocate; + public byte[] byRes = new byte[63]; + + } + + //叠加项具体信息 + public static class NET_ITS_OVERLAP_INFO_PARAM extends Structure{ + public byte[] bySite = new byte[128]; //地点描述 + public byte[] byRoadNum = new byte[32]; //路口编号 + public byte[] byInstrumentNum = new byte[32]; //设备编号 + public byte[] byDirection = new byte[32]; //方向编号 + public byte[] byDirectionDesc = new byte[32]; //方向描述 + public byte[] byLaneDes = new byte[32]; //车道描述 + public byte[] byRes1 = new byte[32]; //保留 + public byte[] byMonitoringSite1 = new byte[44]; //监测点1信息 + public byte[] byMonitoringSite2 = new byte[32]; //监测点2信息 + public byte[] byRes = new byte[64]; //保留 + } + + public static class NET_ITS_OVERLAP_CFG_V50 extends Structure{ + public int dwSize; + public byte byEnable; //是否启用,0-不启用,1-启用 + public byte[] byRes1 = new byte[3]; + public NET_ITS_OVERLAP_ITEM_PARAM_V50 struOverLapItemV50 = new NET_ITS_OVERLAP_ITEM_PARAM_V50(); //字符串参数 + public NET_ITS_OVERLAP_INFO_PARAM struOverLapInfo = new NET_ITS_OVERLAP_INFO_PARAM(); //字符串内容信息 + public byte[] byRes = new byte[120]; + + } + +//人体特征识别结果结构体 +public static class NET_VCA_HUMAN_FEATURE extends Structure { + public byte byAgeGroup; //年龄段,参见 HUMAN_AGE_GROUP_ENUM + public byte bySex; //性别, 0-表示“未知”(算法不支持),1 – 男 , 2 – 女, 0xff-算法支持,但是没有识别出来 + public byte byEyeGlass; //是否戴眼镜 0-表示“未知”(算法不支持),1 – 不戴, 2 – 戴,0xff-算法支持,但是没有识别出来 + //抓拍图片人脸年龄的使用方式,如byAge为15,byAgeDeviation为1,表示,实际人脸图片年龄的为14-16之间 + public byte byAge;//年龄 0-表示“未知”(算法不支持),0xff-算法支持,但是没有识别出来 + public byte byAgeDeviation;//年龄误差值 + public byte byEthnic; //字段预留,暂不开放 + public byte byMask; //是否戴口罩 0-表示“未知”(算法不支持),1 – 不戴, 2 – 戴, 0xff-算法支持,但是没有识别出来 + public byte bySmile; //是否微笑 0-表示“未知”(算法不支持),1 – 不微笑, 2 – 微笑, 0xff-算法支持,但是没有识别出来 + public byte byFaceExpression; /* 表情,参见FACE_EXPRESSION_GROUP_ENUM*/ + public byte byBeard; // 胡子, 0-不支持,1-没有胡子,2-有胡子,0xff-unknow表示未知,算法支持未检出 + public byte byRace; + public byte byHat; // 帽子, 0-不支持,1-不戴帽子,2-戴帽子,0xff-unknow表示未知,算法支持未检出 + public byte[] byRes = new byte[4]; //保留 +} + +//人脸抓拍附加信息结构体 +public static class NET_VCA_FACESNAP_ADDINFO extends Structure { + //人脸矩形框,该坐标为人脸小图(头肩照)中人脸的坐标 + public NET_VCA_RECT struFacePicRect = new NET_VCA_RECT(); + public int iSwingAngle;//旋转角, -90~90度 + public int iTiltAngle;//俯仰角, -90~90度 + public int dwPupilDistance;//瞳距,范围为:最小值为10像素,最大值为当前分辨率宽度/1.6 + public byte byBlockingState;//目标遮挡状态, 0-表示“未知”(算法不支持),1~无遮挡,2~瞬时轻度遮挡,3~持续轻度遮挡,4~严重遮挡 + public byte byFaceSnapThermometryEnabled;//人脸抓拍测温使能 1-开启 0-关闭 + public byte byIsAbnomalTemperature;//人脸抓拍测温是否温度异常 1-是 0-否 + public byte byThermometryUnit;//测温单位: 0-摄氏度(℃),1-华氏度(℉),2-开尔文(K) + public NET_DVR_TIME_EX struEnterTime = new NET_DVR_TIME_EX(); // 最佳抓拍下进入时间 + public NET_DVR_TIME_EX struExitTime = new NET_DVR_TIME_EX(); // 最佳抓拍下离开时间 + public float fFaceTemperature; // 人脸温度( - 20.0℃~150.0℃,精确到小数点后1位) + public float fAlarmTemperature;// 测温报警警阈值(精确到小数点后1位) + public byte[] byRes = new byte[472];// 保留字节 +} + +//人脸抓拍结果 +public static class NET_VCA_FACESNAP_RESULT extends Structure { + public int dwSize; // 结构大小 + public int dwRelativeTime; // 相对时标 + public int dwAbsTime; // 绝对时标 + public int dwFacePicID; //人脸图ID + public int dwFaceScore; //人脸评分,0-100 + public NET_VCA_TARGET_INFO struTargetInfo = new NET_VCA_TARGET_INFO();//报警目标信息 + public NET_VCA_RECT struRect = new NET_VCA_RECT(); //人脸子图区域 + public NET_VCA_DEV_INFO struDevInfo = new NET_VCA_DEV_INFO(); //前端设备信息 + public int dwFacePicLen; //人脸子图的长度,为0表示没有图片,大于0表示有图片 + public int dwBackgroundPicLen; //背景图的长度,为0表示没有图片,大于0表示有图片(保留) + public byte bySmart; //IDS设备返回0(默认值),Smart Functiom Return 1 + public byte byAlarmEndMark;//报警结束标记0-保留,1-结束标记(该字段结合人脸ID字段使用,表示该ID对应的下报警结束,主要提供给NVR使用,用于判断报警结束,提取识别图片数据中,清晰度最高的图片) + public byte byRepeatTimes; //重复报警次数,0-无意义 + public byte byUploadEventDataType;//人脸图片数据长传方式:0-二进制数据,1-URL + public NET_VCA_HUMAN_FEATURE struFeature = new NET_VCA_HUMAN_FEATURE(); //人体属性 + public float fStayDuration; //停留画面中时间(单位: 秒) + public byte[] sStorageIP = new byte[16]; //存储服务IP地址 + public short wStoragePort; //存储服务端口号 + public short wDevInfoIvmsChannelEx; //与NET_VCA_DEV_INFO里的byIvmsChannel含义相同,能表示更大的值。老客户端用byIvmsChannel能继续兼容,但是最大到255。新客户端版本请使用wDevInfoIvmsChannelEx。 + public byte byFacePicQuality; + public byte byUIDLen; // 上传报警的标识长度 + public byte byLivenessDetectionStatus;// 活体检测状态:0-保留,1-未知(检测失败),2-非真人人脸,3-真人人脸,4-未开启活体检测 + /*附加信息标识位(即是否有NET_VCA_FACESNAP_ADDINFO结构体),0-无附加信息, 1-有附加信息。*/ + public byte byAddInfo; + public Pointer pUIDBuffer; //标识指针 + //附加信息指针,指向NET_VCA_FACESNAP_ADDINFO结构体 + public Pointer pAddInfoBuffer; + public byte byTimeDiffFlag; /*时差字段是否有效 0-时差无效, 1-时差有效 */ + public byte cTimeDifferenceH; /*与UTC的时差(小时),-12 ... +14, +表示东区,,byTimeDiffFlag为1时有效*/ + public byte cTimeDifferenceM; /*与UTC的时差(分钟),-30, 30, 45, +表示东区,byTimeDiffFlag为1时有效*/ + public byte byBrokenNetHttp; //断网续传标志位,0-不是重传数据,1-重传数据 + public Pointer pBuffer1; //人脸子图的图片数据 + public Pointer pBuffer2; //背景图的图片数据(保留,通过查找背景图接口可以获取背景图) +} + +//人脸抓拍信息 +public static class NET_VCA_FACESNAP_INFO_ALARM extends Structure { + public int dwRelativeTime; // 相对时标 + public int dwAbsTime; // 绝对时标 + public int dwSnapFacePicID; //抓拍人脸图ID + public int dwSnapFacePicLen; //抓拍人脸子图的长度,为0表示没有图片,大于0表示有图片 + public NET_VCA_DEV_INFO struDevInfo = new NET_VCA_DEV_INFO(); //前端设备信息 + public byte byFaceScore; //人脸评分,指人脸子图的质量的评分,0-100 + public byte bySex;//性别,0-未知,1-男,2-女 + public byte byGlasses;//是否带眼镜,0-未知,1-是,2-否 + //抓拍图片人脸年龄的使用方式,如byAge为15,byAgeDeviation为1,表示,实际人脸图片年龄的为14-16之间 + public byte byAge;//年龄 + public byte byAgeDeviation;//年龄误差值 + public byte byAgeGroup;//年龄段,详见HUMAN_AGE_GROUP_ENUM,若传入0xff表示未知 + public byte byFacePicQuality; + public byte byRes1; // 保留字节 + public int dwUIDLen; // 上传报警的标识长度 + public Pointer pUIDBuffer; //标识指针 + public float fStayDuration; //停留画面中时间(单位: 秒) + public Pointer pBuffer1; //抓拍人脸子图的图片数据 +} + +//籍贯参数 +public static class NET_DVR_AREAINFOCFG extends Structure { + public short wNationalityID; //国籍 + public short wProvinceID; //省 + public short wCityID; //市 + public short wCountyID; //县 + public int dwCode; //国家标准的省份、城市、县级代码,当这个字段不为0的时候,使用这个值,新设备上传这个值表示籍贯参数,老设备这个值为0 +} + +//人员信息 +public int MAX_HUMAN_BIRTHDATE_LEN = 10; +public static class NET_VCA_HUMAN_ATTRIBUTE extends Structure { + public byte bySex; //性别:0-男,1-女 + public byte byCertificateType; //证件类型:0-身份证,1-警官证 + public byte[] byBirthDate = new byte[MAX_HUMAN_BIRTHDATE_LEN]; //出生年月,如:201106 + public byte[] byName = new byte[NAME_LEN]; //姓名 + public NET_DVR_AREAINFOCFG struNativePlace = new NET_DVR_AREAINFOCFG(); //籍贯参数 + public byte[] byCertificateNumber = new byte[NAME_LEN]; //证件号 + public int dwPersonInfoExtendLen;// 人员标签信息扩展长度 + public Pointer pPersonInfoExtend; //人员标签信息扩展信息 + public byte byAgeGroup;//年龄段,详见HUMAN_AGE_GROUP_ENUM,如传入0xff表示未知 + public byte[] byRes2 = new byte[11]; +} + +//黑名单信息 +public static class NET_VCA_BLACKLIST_INFO extends Structure { + public int dwSize; //结构大小 + public int dwRegisterID; //名单注册ID号(只读) + public int dwGroupNo; //分组号 + public byte byType; //黑白名单标志:0-全部,1-白名单,2-黑名单 + public byte byLevel; //黑名单等级,0-全部,1-低,2-中,3-高 + public byte[] byRes1 = new byte[2]; //保留 + public NET_VCA_HUMAN_ATTRIBUTE struAttribute = new NET_VCA_HUMAN_ATTRIBUTE(); //人员信息 + public byte[] byRemark = new byte[NAME_LEN]; //备注信息 + public int dwFDDescriptionLen;//人脸库描述数据长度 + public Pointer pFDDescriptionBuffer;//人脸库描述数据指针 + public int dwFCAdditionInfoLen;//抓拍库附加信息长度 + public Pointer pFCAdditionInfoBuffer;//抓拍库附加信息数据指针(FCAdditionInfo中包含相机PTZ坐标) + public byte[] byRes2 = new byte[4]; +} + +//黑名单报警信息 +public static class NET_VCA_BLACKLIST_INFO_ALARM extends Structure { + public NET_VCA_BLACKLIST_INFO struBlackListInfo = new NET_VCA_BLACKLIST_INFO(); //黑名单基本信息 + public int dwBlackListPicLen; //黑名单人脸子图的长度,为0表示没有图片,大于0表示有图片 + public int dwFDIDLen;// 人脸库ID长度 + public Pointer pFDID; //人脸库Id指针 + public int dwPIDLen;// 人脸库图片ID长度 + public Pointer pPID; //人脸库图片ID指针 + public short wThresholdValue; //人脸库阈值[0,100] + public byte byIsNoSaveFDPicture;//0-保存人脸库图片,1-不保存人脸库图片, 若开启了导入图片或者建模时不保存原图功能时,该字段返回1,此时人脸库图片将不再返回 + public byte byRealTimeContrast;//是否实时报警 0-实时 1-非实时 + public Pointer pBuffer1; //黑名单人脸子图的图片数据 +} + + +//黑名单比对结果报警上传 +public static class NET_VCA_FACESNAP_MATCH_ALARM extends Structure { + public int dwSize; // 结构大小 + public float fSimilarity; //相似度,[0.001,1] + public NET_VCA_FACESNAP_INFO_ALARM struSnapInfo = new NET_VCA_FACESNAP_INFO_ALARM(); //抓拍信息 + public NET_VCA_BLACKLIST_INFO_ALARM struBlackListInfo = new NET_VCA_BLACKLIST_INFO_ALARM(); //黑名单信息 + public byte[] sStorageIP = new byte[16]; //存储服务IP地址 + public short wStoragePort; //存储服务端口号 + public byte byMatchPicNum; //匹配图片的数量,0-保留(老设备这个值默认0,新设备这个值为0时表示后续没有匹配的图片信息) + public byte byPicTransType;//图片数据传输方式: 0-二进制;1-url + public int dwSnapPicLen;//设备识别抓拍图片长度 + public Pointer pSnapPicBuffer;//设备识别抓拍图片指针 + public NET_VCA_RECT struRegion = new NET_VCA_RECT();//目标边界框,设备识别抓拍图片中,人脸子图坐标 + public int dwModelDataLen;//建模数据长度 + public Pointer pModelDataBuffer;// 建模数据指针 + public byte byModelingStatus;// 建模状态 + public byte byLivenessDetectionStatus;//活体检测状态:0-保留,1-未知(检测失败),2-非真人人脸,3-真人人脸,4-未开启活体检测 + public byte cTimeDifferenceH; /*与UTC的时差(小时),-12 ... +14, +表示东区,0xff无效*/ + public byte cTimeDifferenceM; /*与UTC的时差(分钟),-30, 30, 45, +表示东区,0xff无效*/ + public byte byMask; //抓拍图是否戴口罩,0-保留,1-未知,2-不戴口罩,3-戴口罩 + public byte bySmile; //抓拍图是否微笑,0-保留,1-未知,2-不微笑,3-微笑 + public byte byContrastStatus; //比对结果,0-保留,1-比对成功,2-比对失败 + public byte byBrokenNetHttp; //断网续传标志位,0-不是重传数据,1-重传数据 +} + +//交通事件报警(扩展) +public static class NET_DVR_AID_ALARM_V41 extends Structure { + public int dwSize; //结构长度 + public int dwRelativeTime; //相对时标 + public int dwAbsTime; //绝对时标 + public NET_VCA_DEV_INFO struDevInfo = new NET_VCA_DEV_INFO(); //前端设备信息 + public NET_DVR_AID_INFO struAIDInfo = new NET_DVR_AID_INFO(); //交通事件信息 + public NET_DVR_SCENE_INFO struSceneInfo = new NET_DVR_SCENE_INFO(); //场景信息 + public int dwPicDataLen; //图片长度 + public Pointer pImage; //指向图片的指针 + // 0-数据直接上传; 1-云存储服务器URL(3.7Ver)原先的图片数据变成URL数据,图片长度变成URL长度 + public byte byDataType; + public byte byLaneNo; //关联车道号 + public short wMilliSecond; //时标毫秒 + //监测点编号(路口编号、内部编号) + public byte[] byMonitoringSiteID = new byte[MONITORSITE_ID_LEN/*48*/]; + public byte[] byDeviceID = new byte[DEVICE_ID_LEN/*48*/];//设备编号 + public int dwXmlLen;//XML报警信息长度 + public Pointer pXmlBuf;// XML报警信息指针,其XML对应到EventNotificationAlert XML Block + public byte byTargetType;// 检测的目标类型,0~未知,1~行人、2~二轮车、3~三轮车(行人检测中返回) + public byte[] byRes = new byte[19]; // 保留字节 +} + +//交通统计信息报警(扩展) +public static class NET_DVR_TPS_ALARM_V41 extends Structure { + public int dwSize; // 结构体大小 + public int dwRelativeTime; // 相对时标 + public int dwAbsTime; // 绝对时标 + public NET_VCA_DEV_INFO struDevInfo; // 前端设备信息 + public NET_DVR_TPS_INFO_V41 struTPSInfo; // 交通参数统计信息 + //监测点编号(路口编号、内部编号) + public byte[] byMonitoringSiteID = new byte[MONITORSITE_ID_LEN/*48*/]; + public byte[] byDeviceID = new byte[DEVICE_ID_LEN/*48*/];//设备编号 + public int dwStartTime; // 开始统计时间 + public int dwStopTime; // 结束统计时间 + public byte[] byRes = new byte[24]; // 保留 +} + +public static class NET_DVR_LANE_PARAM_V41 extends Structure { + public byte[] byRuleName = new byte[NAME_LEN]; // 车道规则名称 + public byte byRuleID; // 规则序号,为规则配置结构下标,0-7 + public byte byLaneType; // 车道上行或下行 + public byte byTrafficState; // 车道的交通状态,0-无效,1-畅通,2-拥挤,3-堵塞 + public byte byLaneNo; //车道号 + public int dwVaryType; // 车道交通参数变化类型参照 TRAFFIC_DATA_VARY_TYPE_EX_ENUM,按位区分 + public int dwTpsType; // 数据变化类型标志,表示当前上传的统计参数中,哪些数据有效,参照ITS_TPS_TYPE,按位区分 + public int dwLaneVolume; // 车道流量,统计有多少车子通过 + public int dwLaneVelocity; // 车道速度,公里计算 + public int dwTimeHeadway ; // 车头时距,以秒计算 + public int dwSpaceHeadway; // 车头间距,以米来计算 + public float fSpaceOccupyRation; // 车道占有率,百分比计算(空间上) + public float fTimeOccupyRation; // 时间占有率,百分比计算 + public int dwLightVehicle; // 小型车数量 + public int dwMidVehicle; // 中型车数量 + public int dwHeavyVehicle; // 重型车数量 + public NET_DVR_LANE_QUEUE struLaneQueue; // 车道队列长度 + public NET_VCA_POINT struRuleLocation; // 规则位置虚拟线圈的中心 + public int dwOversizeVehicle; // 大型车数量 + public byte[] byRes2 = new byte[60]; // 保留 +} + +public int MAX_TPS_RULE = 8; // 最大参数规则数目 +public static class NET_DVR_TPS_INFO_V41 extends Structure { + public int dwLanNum; // 交通参数的车道数目 + public NET_DVR_LANE_PARAM_V41[] struLaneParam= new NET_DVR_LANE_PARAM_V41[MAX_TPS_RULE]; + public int dwSceneID;//场景ID + public byte[] byRes = new byte[28]; //保留 +} + +// 车道队列结构体 +public static class NET_DVR_LANE_QUEUE extends Structure { + public NET_VCA_POINT struHead; //队列头 + public NET_VCA_POINT struTail; //队列尾 + public int dwLength; //实际队列长度 单位为米 [0-500] +} + +//TPS统计过车数据上传 +public static class NET_DVR_TPS_STATISTICS_INFO extends Structure { + public int dwSize; // 结构体大小 + public int dwChan ;//通道号 + public NET_DVR_TPS_STATISTICS_PARAM struTPSStatisticsInfo;// 交通参数统计信息 + public byte[] byRes = new byte[128]; // 保留 +} + +// 交通参数统计信息 +public static class NET_DVR_TPS_STATISTICS_PARAM extends Structure { + public byte byStart; // 开始码 + public byte byCMD; // 命令号, 08-定时成组数据指令 + public byte[] byRes = new byte[2]; // 预留字节 + public short wDeviceID; // 设备ID + public short wDataLen; // 数据长度 + public byte byTotalLaneNum; // 有效车道总数 + public byte[] byRes1 = new byte[15]; + public NET_DVR_TIME_V30 struStartTime; //统计开始时间 + public int dwSamplePeriod; //统计时间,单位秒 + public NET_DVR_TPS_LANE_PARAM[] struLaneParam = new NET_DVR_TPS_LANE_PARAM[8]; +} + +//统计信息 +public static class NET_DVR_TPS_LANE_PARAM extends Structure { + public byte byLane; // 对应车道号 + public byte bySpeed; // 车道过车平均速度 + public byte[] byRes = new byte[2]; // 保留 + public int dwLightVehicle; // 小型车数量 + public int dwMidVehicle; // 中型车数量 + public int dwHeavyVehicle; // 重型车数量 + public int dwTimeHeadway; // 车头时距,以秒计算 + public int dwSpaceHeadway; // 车头间距,以米来计算 + public float fSpaceOccupyRation; // 空间占有率,百分比计算,浮点数*1000 + public float fTimeOccupyRation; // 时间占有率,百分比计算,浮点数*1000 + public byte[] byRes1 = new byte[16]; // 保留 +} + +public static class NET_DVR_TIME_SEARCH_COND extends Structure { + public short wYear; //年 + public byte byMonth; //月 + public byte byDay; //日 + public byte byHour; //时 + public byte byMinute; //分 + public byte bySecond; //秒 + public byte byLocalOrUTC; //0-时差无效,设备本地时间,即设备OSD时间 1-时差有效 + public short wMillisecond; //毫秒,精度不够,默认为0 + public byte cTimeDifferenceH; //与UTC的时差(小时),-12 ... +14,+表示东区,byLocalOrUTC为1时有效 + public byte cTimeDifferenceM; //与UTC的时差(分钟),-30, 0, 30, 45,+表示东区,byLocalOrUTC为1时有效 +} + +//报警信息查询条件结构体 +public static class NET_DVR_ALARM_SEARCH_COND extends Structure { + public int dwSize; + public NET_DVR_TIME_SEARCH_COND strStartTime; //开始时间,时间为空则代表不通过时间筛选。 + public NET_DVR_TIME_SEARCH_COND strStopTime; //结束时间, 时间为空则代表不通过时间筛选。 + /* + 报警命令,该字段值与报警布防类型相同,目前支持: + COMM_VCA_ALARM 0x4993 智能检测报警 + COMM_UPLOAD_FACESNAP_RESULT 0x1112 人脸识别结果上传 + COMM_SNAP_MATCH_ALAR 0x2902 人脸比对结果上传 + */ + public int dwAlarmComm; //若该命令为空这代表不进行报警命令过滤。 + public byte[] sAlarmUID = new byte[64]; //UID标识(上传报警时设备返回的UID标识,64字节的长度,可以使用时间(精确到毫秒)加上随即数的方式组成),为空则代表不区分UID + public byte[] byRes = new byte[128]; +} + +//报警信息查询结果结构体 +public static class NET_DVR_ALARM_SEARCH_RESULT extends Structure { + public int dwSize; + /* + 报警命令,该字段值与报警布防类型相同,目前支持: + COMM_VCA_ALARM 0x4993 智能检测报警 + COMM_UPLOAD_FACESNAP_RESULT 0x1112 人脸识别结果上传 + COMM_SNAP_MATCH_ALARM 0x2902 人脸比对结果上传 + */ + public int dwAlarmComm; + /* + 报警信息,该字段值与报警信息相同,目前支持: + 当COMM_VCA_ALARM时,该报警信息为JSON报文 + 当COMM_UPLOAD_FACESNAP_RESULT时,该报警信息为NET_VCA_FACESNAP_RESULT + 当COMM_SNAP_MATCH_ALARM时,该报警信息为NET_VCA_FACESNAP_MATCH_ALARM + */ + public int dwAlarmLen;//报警信息,即pAlarmInfo指针指向的数据长度 + public Pointer pAlarmInfo; + public NET_DVR_ALARMER struAlarmer = new NET_DVR_ALARMER(); + public byte[] byRes = new byte[128]; +} + +public static class NET_DVR_ALARM_ISAPI_INFO extends Structure { + public Pointer pAlarmData; // 报警数据(参见下表) + public int dwAlarmDataLen; // 报警数据长度 + public byte byDataType; // 0-invalid,1-xml,2-json + public byte byPicturesNumber; // 图片数量 + public byte[] byRes = new byte[2]; + public Pointer pPicPackData; // 图片变长部分 + //(byPicturesNumber个{NET_DVR_ALARM_ISAPI_PICDATA};) + public byte[] byRes1 = new byte[32]; +} + +public static class NET_DVR_LOCAL_GENERAL_CFG extends Structure { + public byte byExceptionCbDirectly; //0-通过线程池异常回调,1-直接异常回调给上层 + public byte byNotSplitRecordFile; //回放和预览中保存到本地录像文件不切片 0-默认切片,1-不切片 + public byte byResumeUpgradeEnable; //断网续传升级使能,0-关闭(默认),1-开启 + public byte byAlarmJsonPictureSeparate; //控制JSON透传报警数据和图片是否分离,0-不分离,1-分离(分离后走COMM_ISAPI_ALARM回调返回) + public byte[] byRes = new byte[4]; //保留 + public long i64FileSize; //单位:Byte + public int dwResumeUpgradeTimeout; //断网续传重连超时时间,单位毫秒 + public byte[] byRes1 = new byte[236]; //预留 + +} + +public static class NET_DVR_LOCAL_TCP_PORT_BIND_CFG extends Structure { + public short wLocalBindTcpMinPort; //本地绑定Tcp最小端口 + public short wLocalBindTcpMaxPort; //本地绑定Tcp最大端口 + public byte[] byRes = new byte[60]; //保留 +} + + +public static class NET_DVR_LOCAL_CHECK_DEV extends Structure { + public int dwCheckOnlineTimeout; //巡检时间间隔,单位ms 最小值为30s,最大值120s。为0时,表示用默认值(120s) + public int dwCheckOnlineNetFailMax; //由于网络原因失败的最大累加次数;超过该值SDK才回调用户异常,为0时,表示使用默认值1 + public byte[] byRes = new byte[256]; +} + +public static final int MAX_FILE_PATH_LEN = 256; //文件路径长度 +public static class NET_DVR_ALARM_ISAPI_PICDATA extends Structure { + public int dwPicLen; + public byte byPicType; //图片格式: 1- jpg + public byte[] byRes = new byte[3]; + public byte[] szFilename = new byte[MAX_FILE_PATH_LEN]; + public Pointer pPicData; // 图片数据 +} + +public static class NET_DVR_FOCUSMODE_CFG extends Structure +{ + public int dwSize; + public byte byFocusMode; /* 聚焦模式,0-自动,1-手动,2-半自动 */ + public byte byAutoFocusMode; /* 自动聚焦模式,0-关,1-模式A,2-模式B,3-模式AB,4-模式C 自动聚焦模式,需要在聚焦模式为自动时才显示*/ + public short wMinFocusDistance; /* 最小聚焦距离,单位CM, 0-自动,0xffff-无穷远 */ + public byte byZoomSpeedLevel; /* 变倍速度,为实际取值,1-3 */ + public byte byFocusSpeedLevel; /* 聚焦速度,为实际取值,1-3 */ + public byte byOpticalZoom; /* 光学变倍,0-255 */ + public byte byDigtitalZoom; /* 数字变倍,0-255 */ + public float fOpticalZoomLevel; /* 光学变倍(倍率值) [1,32], 最小间隔0.5 ,内部设备交互的时候*1000 */ + public int dwFocusPos;/* dwFocusPos 是focus值(聚焦值),范围为[0x1000,0xC000],这个值是sony坐标值,使用这个值是为了对外统一,保证不同的镜头对外focus值都转换在这个范围内 (手动聚焦模式下下应用)*/ + public byte byFocusDefinitionDisplay;// 聚焦清晰度显示,0~不显示,1~显示, 开启会在码流上显示当前镜头目标的清晰度值,用于帮助客户调焦使相机抓拍能够达到最清晰的效果,该清晰度越大代表着越清晰,清晰度范围为:0~100.0000 + public byte byFocusSensitivity; //聚焦灵敏度,范围[0,2],聚焦模式为自动、半自动时生效 + public byte[] byRes1 = new byte[2]; + public int dwRelativeFocusPos;//相对focus值,其低16位表示聚焦值,0~4000;高16位代表当前聚焦值获取时的温度值 + public byte[] byRes = new byte[48]; +} + +public static class NET_DVR_SERIALSTART_V40 extends Structure +{ + public int dwSize; //结构体大小 + public int dwSerialType; //串口号(1-232串口,2-485串口) + public byte bySerialNum; //串口编号 + public byte[] byRes = new byte[255]; + + +} + +public static class NET_DVR_PRESET_NAME extends Structure { + public int dwSize; + public short wPresetNum; //预置点编号 + public byte[] byRes1 = new byte[2]; //字节对齐 + public byte[] byName = new byte[NAME_LEN]; + public short wPanPos; //水平参数 如果获取到的数据大于360默认减去360 + public short wTiltPos; //垂直参数 如果获取到的数据大于360默认减去360 + public short wZoomPos; //变倍参数如果获取到的数据大于360默认减去360 + public byte[] byRes = new byte[58]; +} + +public static class NET_DVR_DAYTIME extends Structure { + public byte byHour;//0~24 + public byte byMinute;//0~60 + public byte bySecond;//0~60 + public byte byRes; + public short wMilliSecond; //0~1000 + public byte[] byRes1= new byte[2]; +} + +public static class NET_DVR_SCHEDULE_DAYTIME extends Structure { + public NET_DVR_DAYTIME struStartTime; //开始时间 + public NET_DVR_DAYTIME struStopTime; //结束时间 +} + +public static class NET_DVR_BUILTIN_SUPPLEMENTLIGHT extends Structure { + public int dwSize;//结构体大小 + public byte byMode;//补光灯模式 0-定时,1-开启,2-关闭,3-自动(非光敏,算法画面识别) + public byte byBrightnessLimit;//亮度限制[0,100] + public byte bySupplementLightMode;//补光灯类型,0~白光模式,1~混合模式 + public byte byMixedLightRegulatMode;//混合补光灯亮度调节模式,0~自动,1~手动,当bySupplementLightMode = 1时生效 + public byte byLrLightBrightness;//红外亮度控制[0,100],当byMixedLightRegulatMode = 1时生效。 + public byte byHighLrLightBrightness;// 远光红外光亮度配置[0,100],当byMixedLightRegulatMode = 1时生效 + public byte byHighBrightnessLimit;// 远光白光亮度配置[0,100],当byMixedLightRegulatMode = 1时生效 + public byte byLowLrLightBrightness;// 近光红外光亮度配置[0,100],当byMixedLightRegulatMode = 1时生效 + public NET_DVR_SCHEDULE_DAYTIME struSchedTime;//定时时间段 + public byte byLowBrightnessLimit;//近光白光亮度配置[0,100],当byMixedLightRegulatMode = 1时生效 + public byte byWhiteLightBrightness;// 白光灯亮度 + public byte[] byRes1 = new byte[254]; +} + +public static class NET_DVR_HANDLEEXCEPTION_V41 extends Structure { + public int dwHandleType; //异常处理,异常处理方式的"或"结果 + /*0x00: 无响应*/ + /*0x01: 监视器上警告*/ + /*0x02: 声音警告*/ + /*0x04: 上传中心*/ + /*0x08: 触发报警输出*/ + /*0x10: 触发JPRG抓图并上传Email*/ + /*0x20: 无线声光报警器联动*/ + /*0x40: 联动电子地图(目前只有PCNVR支持)*/ + /*0x200: 抓图并上传FTP*/ + /*0x400: 虚交侦测 联动 聚焦模式(提供可配置项,原先设备自动完成)IPC5.1.0*/ + /*0x800: PTZ联动跟踪(球机跟踪目标)*/ + /*0x4000:白光灯报警*/ + /*0x10000:短信报警*/ + public int dwMaxRelAlarmOutChanNum ; //触发的报警输出通道数(只读)最大支持数 + public int[] dwRelAlarmOut = new int[MAX_ALARMOUT_V40]; //触发报警通道 + public byte[] byRes = new byte[64]; //保留 +} + +public static class NET_DVR_PRESETCHAN_INFO extends Structure { + public int dwEnablePresetChan; /*启用预置点的通道, 0xfffffff表示不调用预置点*/ + public int dwPresetPointNo; /*调用预置点通道对应的预置点序号, 0xfffffff表示不调用预置点。*/ +} + +public static class NET_DVR_CRUISECHAN_INFO extends Structure { + public int dwEnableCruiseChan; /*启用巡航的通道*/ + public int dwCruiseNo; /*巡航通道对应的巡航编号, 0xfffffff表示无效*/ +} + +public static class NET_DVR_PTZTRACKCHAN_INFO extends Structure { + public int dwEnablePtzTrackChan; /*启用云台轨迹的通道*/ + public int dwPtzTrackNo; /*云台轨迹通道对应的编号, 0xfffffff表示无效*/ +} + +public static class NET_DVR_EVENT_TRIGGER extends Structure { + public int dwSize;//结构体大小 + public NET_DVR_HANDLEEXCEPTION_V41 struHandleException; //异常处理方式 + public int[] dwRelRecordChan = new int[MAX_CHANNUM_V40]; //实际触发录像通道,按值表示,采用紧凑型排列,从下标0开始顺序读取,中间遇到0xffffffff则后续无效。 + public NET_DVR_PRESETCHAN_INFO[] struPresetChanInfo = new NET_DVR_PRESETCHAN_INFO[MAX_CHANNUM_V40]; //启用的预置点信息 + public NET_DVR_CRUISECHAN_INFO[] struCruiseChanInfo = new NET_DVR_CRUISECHAN_INFO[MAX_CHANNUM_V40]; //启用巡航功能通道的信息 + public NET_DVR_PTZTRACKCHAN_INFO[] struPtzTrackInfo = new NET_DVR_PTZTRACKCHAN_INFO[MAX_CHANNUM_V40]; //调用云台轨迹的通道信息 + public byte byDirection;//触发方向:0-保留;1-全部;2-正向;3-反向 + public byte[] byRes2 = new byte[255]; +} + +public static class NET_DVR_FACELIB_GUARD_COND extends Structure { + public int dwSize; + public int dwChannel; //通道号 + public byte[] szFDID = new byte[68];//人脸库的ID + public byte[] byRes = new byte[128]; +} + +//导入人脸数据条件 +public static class NET_DVR_FACELIB_COND extends Structure { + public int dwSize; + public byte[] szFDID = new byte[NET_SDK_MAX_FDID_LEN/*256*/];//人脸库ID + public byte byConcurrent;//设备并发处理 0-不开启,1-开始 + public byte byCover;//是否覆盖式导入 0-否,1-是 + public byte byCustomFaceLibID;//FDID是否是自定义,0-不是,1-是; + public byte byPictureSaveMode;//上传原图保存模式,0-保存,1-不保存; + public byte[] byIdentityKey = new byte[NET_SDK_MAX_INDENTITY_KEY_LEN/*64*/];//交互操作口令 + public byte[] byRes = new byte[60]; +} + +public static class NET_DVR_SEND_PARAM_IN extends Structure { + public Pointer pSendData; //发送的缓冲区,PicURL == 1 的时候,内存中存储的是 URL 字符串,byUploadModeling == 1 的时候,内存中存储的是 建模base64加密数据 + public int dwSendDataLen; //发送数据长度,PicURL == 1 的时候,表示的 URL 字符串的长度,byUploadModeling == 1 的时候,表示为建模数据base64后的加密长度 + public NET_DVR_TIME_V30 struTime = new NET_DVR_TIME_V30(); //图片时间 + public byte byPicType; //图片格式,1-jpg,2-bmp,3-png,4-SWF,5-GIF + public byte byPicURL; //图片数据采用URL方式 0-二进制图片数据,1-图片数据走URL方式 + /*是否上传建模数据; + 0- 二进制图片数据方式(pSendData指向二进制图片数据, dwPicDataLen为图片二进制数据长度), + 1- 直接上传建模数据(pSendData指向建模base64加密数据, dwPicDataLen为建模数据base64后的加密长度)。 + 注:建模数据采用base64加密方式,选择为建模数据上传后,byPicURL 无需。 + 当”/ISAPI/Intelligent/channels//faceContrast/capabilities”能力中返回isSupportUploadModeling能力节点时,支持上传建模数据. */ + public byte byUploadModeling; + public byte byRes1; + public int dwPicMangeNo; //图片管理号 + public byte[] sPicName = new byte[NAME_LEN]; //图片名称 + public int dwPicDisplayTime; //图片播放时长,单位秒 + public Pointer pSendAppendData; //发送图片的附加信息缓冲区,对应FaceAppendData 的XML描述; + public int dwSendAppendDataLen; //发送图片的附加信息数据长度 FaceAppendData XML的长度; + public byte[] byRes = new byte[192]; +} + +public static class NET_DVR_INQUEST_ROOM extends Structure { + public byte byRoomIndex; //审讯室编号 + public byte byFileType; //0-审讯文件,1-开庭上传文件 + public byte[] byRes = new byte[22]; //保留 +} + +public static class NET_DVR_INQUEST_CDRW_CFG extends Structure { + public int dwSize; + public int dwNum; //刻录机的数量 + public int[] dwRwSelectPara = new int[MAX_CHANNUM_V30];// 是否选中该光驱 + public int dwModeSelect; //0表示循环刻录模式 1表示并行刻录模式(默认模式) + public byte[] byRes= new byte[24]; //保留 + public int dwStartCDRW; //DVR 本地已经开始刻录 + public int dwHdExcp; //硬盘有异 常 + public int dwInterval; //时间间隔,10分钟(0)、20分钟(1)、30分钟(2) + public byte[] sLable= new byte[64]; //光盘名称 +} + +public static class NET_DVR_INQUEST_CDRW_STATUS extends Structure { + /*运行状态:0-审讯开始, + 1-审讯过程中刻录,2-审讯停止, + 3-刻录审讯文件, + 4-备份(事后备份和本地备份) + 5-空闲 + 6-初始化硬盘 + 7-恢复审讯*/ + public int dwType; + public NET_DVR_INQUEST_CDRW[] strCDRWNum = new NET_DVR_INQUEST_CDRW[MAX_INQUEST_CDRW_NUM]; //数组0表示刻录机1 + public NET_DVR_TIME_EX struInquestStartTime = new NET_DVR_TIME_EX(); //审讯开始的时间点 + public byte[] byRes= new byte[16]; //保留 +} + +public static class NET_DVR_INQUEST_CDRW extends Structure { + public int dwEnable; //刻录机状态是否有效,0-无效,1-有效 + public int dwStatus; /*当dwType=0时, 0-光盘正常,1-无光盘或光盘异常, + 当dwType=1或2时,0-刻录正常,1-无光盘或光盘异常,2-光盘已封盘(81不支持),3-光盘空间不足, 4-异常导致审讯终止(81不支持) + 当dwType=3时, 0-刻录正常,1-无光盘或光盘异常,2-光盘已封盘(81不支持),3-光盘空间不足 + 当dwType=4时,0-刻录正常,1-无光盘或光盘异常,2-光盘已封盘(81不支持),3-光盘空间不足 + 当dwType=5时,0-光盘正常, 1-无光盘或光盘异常,2-光盘已封盘(81不支持) + 当dwType=6或7时, 0-刻录正常, 1-无光盘或光盘异常, 2-光盘已封盘(81不支持), 3-光盘空间不足*/ + public int dwVolumn; //光盘容量,单位M + public int dwFreeSpace; //光盘剩余容量,单位M + public int dwTimeLeft; // 光盘剩余时间,单位秒 + public byte byCDType; // 光盘类型 + public byte[] byRes= new byte[3]; //保留字节 +} + + public static class NET_DVR_THERMOMETRY_BASICPARAM extends Structure{ + public int dwSize; + public byte byEnabled; + public byte byStreamOverlay; + public byte byPictureOverlay; + public byte byThermometryRange; + public byte byThermometryUnit; + public byte byThermometryCurve; + public byte byFireImageModea; + public byte byShowTempStripEnable; + public float fEmissivity; + public byte byDistanceUnit; + public byte byEnviroHumidity; + public byte[] byRes2 = new byte[2]; + public NET_DVR_TEMPERATURE_COLOR struTempColor; + public int iEnviroTemperature; + public int iCorrectionVolume; + public byte bySpecialPointThermType; + public byte byReflectiveEnabled; + public short wDistance; + public float fReflectiveTemperature; + public float fAlert; + public float fAlarm; + public float fThermalOpticalTransmittance; + public float fExternalOpticsWindowCorrection; + public byte byDisplayMaxTemperatureEnabled; + public byte byDisplayMinTemperatureEnabled; + public byte byDisplayAverageTemperatureEnabled; + public byte byThermometryInfoDisplayposition; + public int dwAlertFilteringTime; + public int dwAlarmFilteringTime; + public byte byemissivityMode; + public byte bydisplayTemperatureInOpticalChannelEnabled; + public byte[] byRes = new byte[50]; + + } + + public static class NET_DVR_TEMPERATURE_COLOR extends Structure { + public byte byType; + public byte[] byRes1 = new byte[3]; + public int iHighTemperature; + public int iLowTemperature; + public byte[] byRes = new byte[8]; + + } + + //实时温度检测条件参数 +public static class NET_DVR_REALTIME_THERMOMETRY_COND extends Structure { + public int dwSize; /*结构体大小*/ + public int dwChan; /*通道号,从1开始,0xffffffff代表获取全部通道*/ + public byte byRuleID;/*规则ID,0代表获取全部规则,具体规则ID从1开始*/ + public byte byMode; //长连接模式:0- 保留(兼容不支持该功能的老设备),1- 定时模式,2- 温差模式 + public short wInterval; + public byte[] byRes2 = new byte[60]; +} + public static class REMOTECONFIGSTATUS_THERMOMETRY extends Structure { + public byte[] byStatus = new byte[4]; + public byte[] byErrorCode = new byte[4]; + } + +//点测温实时信息 +public static class NET_DVR_POINT_THERM_CFG extends Structure { + public float fTemperature; + public NET_VCA_POINT struPoint; + public byte[] byRes = new byte[120]; +} + +//框/线测温实时信息 +public static class NET_DVR_LINEPOLYGON_THERM_CFG extends Structure { + public float fMaxTemperature; + public float fMinTemperature; + public float fAverageTemperature; + public float fTemperatureDiff; + public NET_VCA_POLYGON struRegion; + public byte[] byRes = new byte[32]; +} + +//实时温度信息 +public static class NET_DVR_THERMOMETRY_UPLOAD extends Structure { + public int dwSize; /* 结构体大小 */ + public int dwRelativeTime; + public int dwAbsTime; + public byte[] szRuleName = new byte[NAME_LEN]; + public byte byRuleID;/* 规则ID,0代表获取全部规则,具体规则ID从1开始 */ + public byte byRuleCalibType; + public short wPresetNo; + public NET_DVR_POINT_THERM_CFG struPointThermCfg; + public NET_DVR_LINEPOLYGON_THERM_CFG struLinePolygonThermCfg; + public byte byThermometryUnit; + public byte byDataType; + public byte byRes1; + public byte bySpecialPointThermType; + public float fCenterPointTemperature; + public float fHighestPointTemperature; + public float fLowestPointTemperature; + public NET_VCA_POINT struHighestPoint; + public NET_VCA_POINT struLowestPoint; + public byte byIsFreezedata; + public byte[] byRes = new byte[95]; +} + +public static class NET_PTZ_INFO extends Structure { + public float fPan; + public float fTilt; + public float fZoom; + public int dwFocus;// 聚焦参数,聚焦范围:归一化0-100000 + public byte[] byRes = new byte[4]; +} + +//手动测温 +public static class NET_SDK_MANUAL_THERMOMETRY extends Structure +{ + public int dwSize; + public int dwChannel; + public int dwRelativeTime; //相对时标(只读) + public int dwAbsTime; //绝对时标(只读) + public byte byThermometryUnit; //测温单位: 0-摄氏度(℃),1-华氏度(℉),2-开尔文(K) + public byte byDataType; //数据状态类型:0-检测中,1-开始,2-结束(只读) + public byte[] byRes1 = new byte[6]; + public NET_SDK_MANUALTHERM_RULE struRuleInfo; //手动测温规则 + public byte[] byRes = new byte[512]; +} + + public static class NET_SDK_MANUALTHERM_RULE extends Structure + { + public byte byRuleID; //规则ID,0-表示无效,从1开始 + public byte byEnable; + public byte[] byRes1= new byte[2]; + public byte[] szRuleName= new byte[NAME_LEN]; + public byte byRuleCalibType; //规则标定类型:0-点,1-框,2-线 + public byte[] byRes2= new byte[3]; + public NET_SDK_POINT_THERMOMETRY struPointTherm; //点测温,当标定类型为0-点时生效 + public NET_SDK_REGION_THERMOMETRY struRegionTherm; //区域测温,当标定类型为1-框、2-线时生效 + public byte[] byRes = new byte[512]; + } + + public static class NET_SDK_POINT_THERMOMETRY extends Structure + { + public float fPointTemperature; //点测温当前温度, 当标定类型为0-点时生效。精确到小数点后一位(-40-1000℃),(浮点数+100)*10 + public NET_VCA_POINT struPoint; //点测温坐标(当规则标定类型为“0-点”的时候生效) + public byte[] byRes = new byte[20]; + } + public static class NET_SDK_REGION_THERMOMETRY extends Structure + { + public float fMaxTemperature; //最高温度,精确到小数点后一位(-40-1000℃),(浮点数+100)*10 + public float fMinTemperature; //最低温度,精确到小数点后一位(-40-1000℃),(浮点数+100)*10 + public float fAverageTemperature; //平均温度,精确到小数点后一位(-40-1000℃),(浮点数+100)*10 + public float fTemperatureDiff; //温差,精确到小数点后一位(-40-1000℃),(浮点数+100)*10 + public NET_VCA_POLYGON struRegion; //区域、线(当规则标定类型为“1-框”或者“2-线”的时候生效) + public byte[] byRes = new byte[20]; + } + +//测温规则温度信息 +public static class NET_DVR_THERMOMETRYRULE_TEMPERATURE_INFO extends Structure +{ + public float fMaxTemperature; + public float fMinTemperature; + public float fAverageTemperature; + public NET_VCA_POINT struHighestPoint; + public NET_VCA_POINT struLowestPoint; + public byte byIsFreezedata; + public byte[] byRes = new byte[15]; +} +//手动测温基本参数配置 +public static class NET_SDK_MANUALTHERM_BASICPARAM extends Structure +{ + public int dwSize; + public short wDistance; + public byte byDistanceUnit; + public byte[] byRes1 = new byte[1]; + public float fEmissivity; + public byte[] byRes = new byte[64]; +} + + +//测温模式配置 +public static class NET_DVR_THERMOMETRY_MODE extends Structure{ + public int dwSize;//结构体大小 + public byte byMode;//测温模式,0~普通模式,1~专家模式 + public byte byThermometryROIEnabled; //测温ROI使能 0-保留 1-不开启 2-开启(基于互斥兼容考虑) + public byte[] byRes = new byte[62]; +} + +public static class NET_DVR_THERMOMETRY_COND extends Structure { + public int dwSize;//结构体大小 + public int dwChannel; + public short wPresetNo;//0-保留 + public byte[] byRes = new byte[62]; +} + +public static class NET_DVR_THERMOMETRY_PRESETINFO_PARAM extends Structure { + public byte byEnabled; //是否使能:0- 否,1- 是 + public byte byRuleID;//规则ID 0-表示无效,从1开始 (list内部判断数据有效性) + public short wDistance;//距离(m)[0, 10000] + public float fEmissivity;//发射率(发射率 精确到小数点后两位)[0.01, 1.00](即:物体向外辐射能量的本领) + public byte byDistanceUnit;//距离单位: 0-米(m),1-英尺(feet),2-厘米(centimeter) + public byte[] byRes = new byte[2]; + public byte byReflectiveEnabled;//反射温度使能:0- 否,1- 是 + public float fReflectiveTemperature;//反射温度 精确到小数后2位 + public byte[] szRuleName = new byte[NAME_LEN/*32*/];//规则名称 + public byte byemissivityMode; //发射率配置类型 1-粗糙,2-较粗糙,3-较光滑, 4-光滑, 0xff-自定义 + public byte[] byRes1 = new byte[62]; + public byte byRuleCalibType;//规则标定类型 0-点,1-框,2-线 + public NET_VCA_POINT struPoint = new NET_VCA_POINT();//点测温坐标(当规则标定类型为"点"的时候生效) + public NET_VCA_POLYGON struRegion = new NET_VCA_POLYGON();//区域、线(当规则标定类型为"框"或者"线"的时候生效) +} + +public static class NET_DVR_THERMOMETRY_PRESETINFO extends Structure { + public int dwSize;//结构体大小 + public short wPresetNo;//0-保留 + public byte[] byRes = new byte[2]; + public NET_DVR_THERMOMETRY_PRESETINFO_PARAM[] struPresetInfo = new NET_DVR_THERMOMETRY_PRESETINFO_PARAM[40]; +} + //设备抓图附加全屏测温数据结构体 + public static class NET_DVR_JPEGPICTURE_WITH_APPENDDATA extends Structure + { + public int dwSize; + public int dwChannel;//通道号 + public int dwJpegPicLen;//Jpeg图片长度 + public Pointer pJpegPicBuff;//Jpeg图片指针 + public int dwJpegPicWidth; // 图像宽度 + public int dwJpegPicHeight; //图像高度 + public int dwP2PDataLen;//全屏测温数据长度 + public Pointer pP2PDataBuff; //全屏测温数据指针 + public byte byIsFreezedata;//是否数据冻结 0-否 1-是 + public byte[] byRes1 = new byte[3]; + public int dwVisiblePicLen;//可见光图片长度 + public Pointer pVisiblePicBuff; //可见光数据指针 + public byte[] byRes2 = new byte[4]; + public NET_VCA_RECT struThermalValidRect; + public NET_VCA_RECT struVisibleValidRect; + public byte[] byRes = new byte[208]; + } +//全屏测温数据解析 + public static class DATE_TIME extends Structure { + public short year; /*APP->DSP 年*/ + public short month; /*APP->DSP 月*/ + public short dayOfWeek; /*APP->DSP 0:星期日-6:星期六*/ + public short day; /*APP->DSP 日*/ + public short hour; /*APP->DSP 小时*/ + public short minute; /*APP->DSP 分钟*/ + public short second; /*APP->DSP 秒*/ + public short milliSecond; /*APP->DSP 毫秒*/ + } + + public static class STREAM_FS_SUPPLE_INFO_TEMP extends Structure { + public int u32TmDataMode; /* 0为4字节,1为2字节 */ + public int u32TmScale; /* 测温缩放比例 */ + public int u32TmOffset; /* 测温偏移量,当前固定为0 */ + public int byIsFreezedata; /*是否是冻结数据,1:冻结,0:非冻结*/ + } + + public static class STREAM_RT_DATA_INFO_S extends Structure { + public int u32RTDataType; // 1-14bit裸数据; 2-全屏测温结果数据; 3-YUV数据 + public int u32FrmNum; + public int u32StdStamp; //DSP相对时间戳 + public DATE_TIME stTime; //绝对时间戳 + public int u32Width; + public int u32Height; + public int u32Len; + public int u32Fps; + public int u32Chan; + } + + public static class STREAM_FARME_INFO_TEMP extends Structure { + public int u32MagicNo; //0x70827773 "FRMI"的ascll码 + public int u32HeaderSize; //结构体长度 + public int u32StreamType; //数据类型: h264/h265, JPEG, Audio, MetaData, RTData: 参见 STREAM_TYPE_E + public int u32StreamLen; //数据长度 + public STREAM_RT_DATA_INFO_S stRTDataInfo; + public STREAM_FS_SUPPLE_INFO_TEMP stFsSuppleInfo; + public int[] res = new int[12]; + public int u32CrcVal; //结构体校验码 对结构体前面数据进行校验 + } + +//温度报警(检测温度和配置温度比较报警) +public static class NET_DVR_THERMOMETRY_ALARM extends Structure { + public int dwSize; + public int dwChannel;//通道号 + public byte byRuleID;//规则ID + public byte byThermometryUnit;//测温单位: 0-摄氏度(℃),1-华氏度(℉),2-开尔文(K) + public short wPresetNo; //预置点号 + public NET_PTZ_INFO struPtzInfo = new NET_PTZ_INFO();//ptz坐标信息 + public byte byAlarmLevel;//0-预警 1-报警 + public byte byAlarmType;/*报警类型 0-最高温度 1-最低温度 2-平均温度 3-温差 4-温度突升 5-温度突降*/ + public byte byAlarmRule;//0-大于,1-小于 + public byte byRuleCalibType;//规则标定类型 0-点,1-框,2线 + public NET_VCA_POINT struPoint = new NET_VCA_POINT();//点测温坐标(当规则标定类型为点的时候生效) + public NET_VCA_POLYGON struRegion = new NET_VCA_POLYGON();//区域(当规则标定类型为框的时候生效) + public float fRuleTemperature;/*配置规则温度,精确到小数点后一位(-40-1000),(浮点数+100) */ + public float fCurrTemperature;/*当前温度,精确到小数点后一位(-40-1000),(浮点数+100) */ + public int dwPicLen;//可见光图片长度 + public int dwThermalPicLen;//热成像图片长度 + public int dwThermalInfoLen;//热成像附加信息长度 + public Pointer pPicBuff; ///可见光图片指针 + public Pointer pThermalPicBuff;// 热成像图片指针 + public Pointer pThermalInfoBuff; //热成像附加信息指针 + public NET_VCA_POINT struHighestPoint = new NET_VCA_POINT();//线、框测温最高温度位置坐标(当规则标定类型为线、框的时候生效) + public float fToleranceTemperature;/* 容差温度,精确到小数点后一位(-40-1000),(浮点数+100) */ + public int dwAlertFilteringTime;//温度预警等待时间 单位秒 范围为0-200秒,默认为0秒 + public int dwAlarmFilteringTime;//温度报警等待时间 单位秒 范围为0-200秒,默认为0秒 + public int dwTemperatureSuddenChangeCycle;//温度突变记录周期,单位秒 + public float fTemperatureSuddenChangeValue;//温度突变值,精确到小数点后一位(大于0) + public byte byPicTransType; //图片数据传输方式: 0-二进制;1-url + public byte[] byRes = new byte[39]; +} + +public static class NET_DVR_FIREDETECTION_ALARM extends Structure { + public int dwSize; + public int dwRelativeTime; + public int dwAbsTime; + public NET_VCA_DEV_INFO struDevInfo; + public short wPanPos; + public short wTiltPos; + public short wZoomPos; + public byte byPicTransType; //图片数据传输方式: 0-二进制;1-url + public byte byRes1; + public int dwPicDataLen; + public Pointer pBuffer; + public NET_VCA_RECT struRect; + public NET_VCA_POINT struPoint; + public short wFireMaxTemperature; + public short wTargetDistance; + public byte byStrategyType; + public byte byAlarmSubType; + public byte byPTZPosExEnable; + public byte byRes2 ; + public NET_PTZ_INFO struPtzPosEx; + public int dwVisiblePicLen; + public Pointer pVisiblePicBuf; + public Pointer pSmokeBuf; + public short wDevInfoIvmsChannelEx; + public byte byRes3; + public byte byFireScanWaitMode; + public int dwVisibleChannel; + public byte byTimeDiffFlag; + public char cTimeDifferenceH; + public char cTimeDifferenceM; + public byte[] byRes = new byte[49]; + +} + +public static class NET_DVR_ARRAY_LIST extends Structure { + public int dwSize; // 结构体大小 + public int dwCount; // 阵列个数 + public NET_DVR_ARRAY_INFO[] struArrayInfo = new NET_DVR_ARRAY_INFO[SUPPORT_ARRAY_NUM]; +} + +public static class NET_DVR_BGA_INFO extends Structure{ + public byte byBga; // 后台任务及类型 + public byte byBgaState; /*函数返回值--后台任务状态*/ + public short wBgaPercentage; /*函数返回值--后台任务执行百分比*/ + public byte[] byRes = new byte[4]; // 保留字节 +} + +// 阵列信息 +public static class NET_DVR_ARRAY_INFO extends Structure{ + public short wArrayID; // 阵列ID + public byte byRaidMode; // raid模式 参照RAID_MODE + public byte byStatus; // 0-在线 1-磁盘丢失 2-下线 3-降级 4-异常 5-次正常 6-外来盘 7-已删除 8-SMART状态异常 0xff-不存在 + public int dwHCapacity; // 阵列容量高32位 + public int dwLCapacity; // 阵列容量低32位 + public int dwHFreeSpace; // 阵列剩余空间高32位 + public int dwLFreeSpace; // 阵列剩余空间高32位 + public byte[] byArrayName = new byte[MAX_NAMELEN]; // 阵列名称 + public byte byPDCount; // 物理磁盘数目 + public byte bySpareCount; // 热备数目 + public byte[] byRes1 = new byte[2]; + public short[] wPDSlots = new short[SUPPORT_PD_NUM]; // 物理磁盘索引 + public short[] wSparePDSlots = new short[SUPPORT_PD_NUM]; // 热备磁盘索引 + public NET_DVR_BGA_INFO struBgaInfo; // 后台任务运行状态 + public short[] wPDSlotsPartTwo = new short[SUPPORT_PD_NUM_PARTTWO]; //物理磁盘索引扩展,0表示无效 + public short[] wSparePDSlotsPartTwo = new short[SUPPORT_PD_NUM_PARTTWO]; // 热备磁盘索引扩展,0表示无效 + public byte[] byRes2 = new byte[48]; // 保留字节 +} + +//物理磁盘 +public static class NET_DVR_PHY_DISK_INFO extends Structure{ + public short wPhySlot; // 硬盘槽位 + public byte byType; // 硬盘信息;0 普通,1全局热备,2-阵列热备 3-阵列盘 + public byte byStatus; // 硬盘状态; 0-正常 1-降级 2-已删除 3-磁盘丢失 4-下线 5-次正常 6-外来 7-异常 8-SMART状态异常 9-休眠 10-有坏块 0xff-不存在 + public byte[] byMode = new byte[40]; // 硬盘类型 字符串 + public int dwHCapacity; // 磁盘总量高32位 单位kb + public int dwLCapacity; // 磁盘总量低32位 + public byte[] byArrrayName = new byte[MAX_NAMELEN]; + public short wArrayID; // 所属阵列ID + public byte byArrayInformation; // 是否含有阵列信息:0 否,1是 + public byte[] byRes = new byte[101]; // 保留字节 +} + +public static class NET_DVR_WORKSTATE_V40 extends Structure{ + public int dwSize ; //结构体大小 + public int dwDeviceStatic; //设备的状态,0-正常,1-CPU占用率太高,超过85%,2-硬件错误,例如串口死掉 + public NET_DVR_DISKSTATE[] struHardDiskStatic = new NET_DVR_DISKSTATE[MAX_DISKNUM_V30]; //硬盘状态,一次最多只能获取33个硬盘信息 + public NET_DVR_CHANNELSTATE_V30[] struChanStatic = new NET_DVR_CHANNELSTATE_V30[MAX_CHANNUM_V40/*512*/];//通道的状态,从前往后顺序排列 + public int[] dwHasAlarmInStatic = new int[MAX_ALARMIN_V40]; //有报警的报警输入口,按值表示,按下标值顺序排列,值为0xffffffff时当前及后续值无效 + public int[] dwHasAlarmOutStatic = new int[MAX_ALARMOUT_V40]; //有报警输出的报警输出口,按值表示,按下标值顺序排列,值为0xffffffff时当前及后续值无效 + public int dwLocalDisplay; //本地显示状态,0-正常,1-不正常 + public byte[] byAudioInChanStatus = new byte[MAX_AUDIO_V30/*2*/]; //按位表示语音通道的状态 0-未使用,1-使用中,第0位表示第1个语音通道 + public byte[] byRes1 = new byte[2]; + public float fHumidity; //传感器获知的湿度,范围:0.0 ~100.0 + public float fTemperature; //传感器获知的温度,范围:-20.0 ~ 90.0 + public byte[] byRes = new byte[116]; //保留 +} + +public static class NET_DVR_GETWORKSTATE_COND extends Structure{ + public int dwSize ; //结构体长度 + public byte byFindHardByCond; /*0-查找全部磁盘(但一次最多只能查找33个),此时dwFindHardStatusNum无效*/ + public byte byFindChanByCond ; /*0-查找全部通道,此时dwFindChanNum无效*/ + public byte[] byRes1 = new byte[2] ;//保留 + public int[] dwFindHardStatus = new int[MAX_DISKNUM_V30/*33*/] ; /*要查找的硬盘号,按值表示,该值采用顺序排列, 遇到0xffffffff则认为后续无效 */ + public int[] dwFindChanNo = new int[MAX_CHANNUM_V40/*512*/] ; /*要查找的通道号,按值表示,该值采用顺序排列, 遇到0xffffffff则认为后续无效 */ + public byte[] byRes = new byte[64] ; //保留 +} + +//多边型结构体 +public static class NET_ITC_POLYGON extends Structure { + public int dwPointNum; //有效点 大于等于3,若是3点在一条线上认为是无效区域,线交叉认为是无效区域 + public NET_VCA_POINT[] struPos = new NET_VCA_POINT[ITC_MAX_POLYGON_POINT_NUM]; //多边形边界点,最多20个 +} + +public static class CUSTOM_uRegion extends Union { + public NET_VCA_RECT struRect = new NET_VCA_RECT(); + public NET_ITC_POLYGON struPolygon = new NET_ITC_POLYGON(); +} + +public static class NET_ITC_PLATE_RECOG_REGION_PARAM extends Structure{ + public byte byMode; //区域类型,0-矩形,1-多边形 + public byte[] byRes1 = new byte[3]; + public CUSTOM_uRegion uRegion = new CUSTOM_uRegion(); + public byte[] byRes = new byte[16]; //保留 +} + +//单组IO测速参数 +public static class NET_ITC_SINGLE_IOSPEED_PARAM extends Structure{ + public byte byEnable; //是否启用,0-不启用,1-启用 + public byte byTrigCoil1; //第一线圈关联IO,0-IO1,1-IO2,2-IO3,3-IO4,4-IO5,5-IO6 + public byte byCoil1IOStatus;//第一线圈IO输入口状态,0-下降沿(默认),1-上升沿,2-上升沿和下降沿,3-高电平,4-低电平 + public byte byTrigCoil2; //第二线圈关联IO,0-IO1,1-IO2,2-IO3,3-IO4,4-IO5,5-IO6 + public byte byCoil2IOStatus;//第二线圈IO输入口状态,0-下降沿(默认),1-上升沿,2-上升沿和下降沿,3-高电平,4-低电平 + public byte byRelatedDriveWay;//关联的车道号 + public byte byTimeOut;//超时时间(默认10),单位s + public byte byRelatedIOOutEx;//第0位表示IO输出口1,以此类推,0-不关联,1-关联 支持关联到8个(兼容byRelatedIOOut字段) + public int dwDistance;//线圈距离(默认1000),单位:厘米 + public byte byCapSpeed;//起拍速度(默认30),单位km/h + public byte bySpeedLimit;//限速值(默认60),单位km/h + public byte bySpeedCapEn; //是否启用超速抓拍,0-否,1-是 + public byte bySnapTimes1; //线圈1抓拍次数(默认不抓拍),0-不抓拍,非0-连拍次数,最大5次 + public byte bySnapTimes2; //线圈2抓拍次数(默认1),0-不抓拍,非0-连拍次数,最大5次 + public byte byBigCarSpeedLimit; //大车车速限制值 + public byte byBigCarSignSpeed;//标志限速(大车),单位km/h(3.7Ver) + public byte byIntervalType; //间隔类型(默认按时间),0-时间起效,1-距离起效 + public short[] wInterval1 = new short[MAX_INTERVAL_NUM];//线圈1连拍间隔时间(单位ms)或连拍间隔距离(单位分米),当byIntervalType为0时,表示间隔时间,当byIntervalType为1时,表示距离 + public short[] wInterval2 = new short[MAX_INTERVAL_NUM];//线圈2连拍间隔时间(单位ms)或连拍间隔距离(单位分米),当byIntervalType为0时,表示间隔时间,当byIntervalType为1时,表示距离 + public byte[] byRelatedIOOut = new byte[MAX_IOOUT_NUM]; //关联的IO输出口(可以同时关联多个),数组0表示IO输出口1,数组1表示IO输出口2,以此类推,0-不关联,1-关联 + public byte byFlashMode; //闪光灯闪烁模式,0-同时闪,1-轮流闪 + public byte byLaneType; //车道类型,0-未配置、1-高速公路、2-城市快速路、0xff-其他道路 + public byte byCarSignSpeed;//标志限速,单位km/h(3.7Ver) + public byte byUseageType; //车道用途类型,详见ITC_LANE_USEAGE_TYPE + public NET_ITC_PLATE_RECOG_REGION_PARAM[] struPlateRecog = new NET_ITC_PLATE_RECOG_REGION_PARAM[MAX_LANEAREA_NUM]; //牌识参数(可用牌识区域1个,保留一个) + //关联车道方向类型,参考ITC_RELA_LANE_DIRECTION_TYPE + //该参数为车道方向参数,与关联车道号对应,确保车道唯一性。 + public byte byRelaLaneDirectionType; + public byte byLowSpeedLimit; //小车限底速值,单位km/h + public byte byBigCarLowSpeedLimit; //大车限底速值,单位km/h + public byte byLowSpeedCapEn; //是否启用低速抓拍,0-否,1-是 + public byte byEmergencyCapEn; //是否启用应急车道抓拍,0-否,1-是 + public byte[] byRes = new byte[27]; +} + +//牌识参数 +public static class NET_ITC_PLATE_RECOG_PARAM extends Structure{ + public byte[] byDefaultCHN = new byte[MAX_CHJC_NUM]; /*设备运行省份的汉字简写*/ + public byte byEnable; //是否启用该区域牌识,0-否,1-是 + public int dwRecogMode; + /*识别的类型, + bit0-背向识别:0-正向车牌识别,1-背向识别(尾牌识别) ; + bit1-大车牌识别或小车牌识别:0-小车牌识别,1-大车牌识别 ; + bit2-车身颜色识别:0-不采用车身颜色识别,在背向识别或小车牌识别时禁止启用,1-车身颜色识别; + bit3-农用车识别:0-不采用农用车识别,1-农用车识别; + bit4-模糊识别:0-不采用模糊识别,1-模糊识别; + bit5-帧定位或场定位:0-帧定位,1-场定位; + bit6-帧识别或场识别:0-帧识别,1-场识别; + bit7-晚上或白天:0-白天,1-晚上 + bit8-摩托车识别:0-不采用摩托车识别,1-摩托车识别; + bit9-场景模式:0-电警/多帧,1-卡口; + bit10-微小车牌:0-不启用,1-启用微小车牌识别(像素60~80) + bit11-安全带检测:0-不启用,1-启用安全带检测 + bit12-民航车牌识别: 0-不启用,1-开启民航车牌识别 + bit13-车牌过渡倾斜处理: 0-不启用,1-开启过渡倾斜处理(PRS) + bit14-超大车牌识别: 0-不启用,1-开启超大车牌识别(PRS) + bit15-遮阳板检测:0-不启用,1-启用遮阳板检测 + bit16-黄标车检测:0-不启用,1-启用黄标车检测 + bit17-危险品车辆检测:0-不启用,1-启用危险品车辆检测 + bit18-使馆车牌识别:0-不启用,1-启用使馆车牌识别 + bit19-车辆子品牌识别:0-不启用,1-启用车辆子品牌识别 + bit20-打电话识别:0-不启用,1-启用 + bit21-车窗悬挂物识别:0-不启用,1-启用 + */ + public byte byVehicleLogoRecog;//车标识别 0-不启用,1-启用 + /* + 0-保留,1-澳,2-京,3-渝,4-闽,5-甘,6-粤,7-桂,8-贵,9-琼,10-冀,11-豫, + 12-黑,13-鄂,14-湘,15-吉,16-苏,17-赣,18-辽,19-蒙,20-宁,21-青,22-鲁, + 23-晋,24-陕,25-沪,26-川,27-台,28-津,29-藏,30-港,31-新,32-云,33-浙, + 34-皖,0xff-全部 + */ + public byte byProvince;//省份索引值 + public byte byRegion;// 区域索引值 0-保留,1-欧洲,2-俄语区域, 3-欧洲&俄罗斯(EU&CIS),4-中东(Middle East) + public byte byCountry;//国家索引,参照枚举COUNTRY_INDEX(不支持“COUNTRY_ALL = 0xff,//ALL 全部”) + public short wPlatePixelWidthMin;//车牌像素识别宽度最小值(单位是像素)当前推荐范围[130,500] + public short wPlatePixelWidthMax;//车牌像素识别宽度最大值(单位是像素)当前推荐范围[130,500] + public byte[] byRes = new byte[24]; +} + +//卡口IO测速参数 +public static class NET_ITC_POST_IOSPEED_PARAM extends Structure{ + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog; //牌识参数 + public NET_ITC_SINGLE_IOSPEED_PARAM[] struSingleIOSpeed = new NET_ITC_SINGLE_IOSPEED_PARAM[MAX_IOSPEED_GROUP_NUM]; //单个IO测速组参数 + public byte[] byRes = new byte[32]; +} + +public static class NET_DVR_GEOGLOCATION extends Structure{ + public int[] iRes = new int[2]; /*保留*/ + public int dwCity; /*城市,详见PROVINCE_CITY_IDX */ +} + +public static class NET_ITC_INTERVAL_PARAM extends Structure{ + public byte byIntervalType; //间隔类型(默认按时间),0-时间起效,1-距离起效 + public byte[] byRes1 = new byte[3]; + public short[] wInterval = new short[MAX_INTERVAL_NUM];//连拍间隔时间(单位ms)或连拍间隔距离(单位分米),当byIntervalType为0时,表示间隔时间,当byIntervalType为1时,表示距离 + public byte[] byRes = new byte[8]; +} + +public static class NET_ITC_VTLANE_PARAM extends Structure{ + public byte byRelatedDriveWay;//关联的车道号 + public byte bySpeedCapEn; //是否启用超速抓拍,0-否,1-是 + public byte bySignSpeed;//标志限速,单位km/h + public byte bySpeedLimit;//限速值,单位km/h + public byte bySnapTimes; //抓拍次数(默认1),0-不抓拍,非0-连拍次数,最大5 + public byte byBigCarSignSpeed;///*大车标志限速,单位km/h*/ + public byte byBigCarSpeedLimit;/*大车限速值,单位km/h*/ + public byte byRelatedIOOutEx;//第0位表示IO输出口1,以此类推,0-不关联,1-关联 支持关联到8个(兼容byRelatedIOOut字段) + public NET_ITC_INTERVAL_PARAM struInterval = new NET_ITC_INTERVAL_PARAM(); //抓拍间隔参数 + public byte[] byRelatedIOOut = new byte[MAX_IOOUT_NUM]; //关联的IO输出口,可以同时关联多个 + public byte byFlashMode; //闪光灯闪烁模式,0-同时闪,1-轮流闪 + public byte byLowSpeedLimit;/*限低速,单位km/h*/ + public byte byBigCarLowSpeedLimit; /*大车限低速,单位km/h*/ + //关联车道方向类型,参考ITC_RELA_LANE_DIRECTION_TYPE + //该参数为车道方向参数,与关联车道号对应,确保车道唯一性。 + public byte byRelaLaneDirectionType; + public NET_ITC_PLATE_RECOG_REGION_PARAM[] struPlateRecog = new NET_ITC_PLATE_RECOG_REGION_PARAM[MAX_LANEAREA_NUM]; //车道牌识参数 + public NET_VCA_LINE struLine = new NET_VCA_LINE(); //车道线 +} + +public static class NET_ITC_VTCOIL_INFO extends Structure{ + public NET_VCA_RECT struLaneRect = new NET_VCA_RECT(); /*虚拟线圈区域*/ + public byte byTrigFlag; //触发标志,0-车头触发;1-车尾触发;2-车头/车尾都触发 + public byte byTrigSensitive; //触发灵敏度,1-100 + public byte[] byRelatedIOOut = new byte[MAX_IOOUT_NUM]; //关联的IO输出口(可以同时关联多个),数组0表示IO输出口1,数组1表示IO输出口2,以此类推,0-不关联,1-关联 + public byte byFlashMode; //闪光灯闪烁模式,0-同时闪,1-轮流闪 + public byte byLaneType; //车道类型,0-未配置、1-高速公路、2-城市快速路、0xff-其他道路 + public byte byEnableRadar; //是否启用雷达测速,0-否,1-是 + public NET_ITC_VTLANE_PARAM struLane = new NET_ITC_VTLANE_PARAM(); //关联的车道参数 + //车道用途类型,详见ITC_LANE_USEAGE_TYPE,使用1和8两种类型(3.7Ver) + public byte byUseageType; + //车辆行驶方向,详见ITC_LANE_CAR_DRIVE_DIRECT(3.7Ver) + public byte byCarDriveDirect; + public byte[] byRes = new byte[30]; +} + +public static class NET_ITC_RADAR_PARAM extends Structure{ + public byte byRadarType; //雷达类型,0-无雷达,1-安道雷雷达,2-奥利维亚,3-川速微波4,雷达接IO扩展盒(此参数在卡口虚拟线圈、混行卡口界面中使用,卡口RS485雷达不使用),0xff-其它类型 + public byte byLevelAngle; //与水平线所成角度,默认为25°(0到90度) + public short wRadarSensitivity; //雷达灵敏度 + public short wRadarSpeedValidTime;//雷达速度有效时间(0~2000] ,0表示不支持 + public byte[] byRes1 = new byte[2]; + public float fLineCorrectParam;//线性矫正参数[0.0~2.0] + public int iConstCorrectParam;//常量矫正参数[-100~100] + public byte[] byRes2 = new byte[8]; +} + +//卡口虚拟线圈触发参数 +public static class NET_ITC_POST_VTCOIL_PARAM extends Structure{ + public byte byRelatedLaneNum;//关联的车道个数 + public byte byIsDisplay; //视频中是否显示虚拟线圈,0-不显示,1-显示 + public byte byLoopPos; //晚间触发线圈的偏向(默认10) + public byte byPolarLenType; /*偏振镜类型,0:不加偏振镜;1:加施耐德偏振镜。*/ + public byte byDayAuxLightMode; /*白天辅助照明模式,0:无辅助照明;1:LED灯照明;2:闪光灯照明*/ + public byte byVideoLaneNO; //视频参考亮度的参考车道号 + public byte byVideoLowTh; /*视频参考亮度低阈值初始化值(默认40)*/ + public byte byVideoHighTh; /*视频参考亮度高阈值初始化值(默认55)*/ + public byte byRecordMode; //录像标志:0-不录像,1-录像 + public byte bySnapMode;//抓拍模式:0-频闪模式;1-爆闪模式 + /*测速方式:0-不测速,0x1-雷达测速,0x2-视频测速*/ + public byte bySpeedDetector; + public byte byRes2; + public short wResolutionX;/* 设备当前分辨率宽*/ + public short wResolutionY;/* 设备当前分辨率高*/ + public int dwDayInitExp; /*视频白天曝光时间的初始值2000*/ + public int dwDayMaxExp; /*视频白天曝光时间的最大值20000*/ + public int dwNightExp; /*晚间视频曝光时间的设置值3000*/ + public int dwSnapExp; /*抓拍曝光时间*/ + public byte byDayInitGain; /*视频白天增益的初始值200*/ + public byte byDayMaxGain; /*视频白天增益的最大值400*/ + public byte byNightGain; /*晚间视频增益*/ + public byte bySnapGain; /*抓拍增益*/ + public int dwSceneMode; //场景模式, 详见SCENE_MODE + public NET_DVR_GEOGLOCATION struGeogLocation = new NET_DVR_GEOGLOCATION(); //地址位置(默认浙江) + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog = new NET_ITC_PLATE_RECOG_PARAM(); //牌识参数 + public NET_ITC_VTCOIL_INFO[] struVtCoil = new NET_ITC_VTCOIL_INFO[MAX_VL_NUM]; //虚拟线圈参数 + public NET_ITC_RADAR_PARAM struRadar = new NET_ITC_RADAR_PARAM(); //雷达参数 + public NET_VCA_LINE struLine = new NET_VCA_LINE(); //右车道线 + //违规检测类型,按位表示,详见ITC_VIOLATION_DETECT_TYPE,0-不启用,1-启用(3.7Ver) + public int dwVioDetectType; + public byte byDebugMode; /*调试模式,0-不启用,1-启用*/ + public byte[] byRes = new byte[11]; +} + +//车道属性参数结构 +public static class NET_ITC_LANE_LOGIC_PARAM extends Structure{ + public byte byUseageType; //车道用途类型,详见ITC_LANE_USEAGE_TYPE + public byte byDirectionType; //车道方向类型,详见ITC_LANE_DIRECTION_TYPE + public byte byCarDriveDirect; //车辆行驶方向,详见ITC_LANE_CAR_DRIVE_DIRECT + public byte[] byRes = new byte[33]; //保留 +} + +//视频电警线结构 +public static class NET_ITC_LINE extends Structure{ + public NET_VCA_LINE struLine = new NET_VCA_LINE(); //线参数 + public byte byLineType; //线类型,详见ITC_LINE_TYPE + public byte[] byRes = new byte[7]; +} + +public static class NET_ITC_SNAPMODE_PARAM extends Structure{ + public byte byVehicleCapMode;//机动车抓拍模式,0-频闪模式;1-爆闪模式 + public byte byNoVehicleCapMode;//非机动车抓拍模式,0-频闪模式;1-爆闪模式 + public byte byPasserCapMode;//行人抓拍模式,0-频闪模式;1-爆闪模式 + public byte[] byRes = new byte[29]; +} + +//size = 128 +public static class NET_ITC_HVT_EC_PARAM extends Structure{ + public int dwCapShutter; //抓拍快门0~65535 + public short wCapGain; //抓拍增益0~100 + public byte[] byRes = new byte[2]; + public int dwDayTimeVideoShutter; //白天曝光时间最大值 + public short wDayTimeVideoGain; //白天增益最大值 + public short wNightVideoGain; //晚上增益最大值 + public short wNightVideoShutter; //晚上曝光时间最大值 + public byte[] byRes1 = new byte[108]; +} + +public static class NET_ITC_LANE_HVT_PARAM extends Structure{ + public byte byLaneNO; //关联的车道号 1~255(用于叠加和上传) + public byte bySignSpeed; //标志限速,单位km/h 0~255 70 + public byte bySpeedLimit; //限速值,单位km/h 0~255 80 实际起效 + public byte byBigCarSignSpeed;///*大车标志限速,单位km/h*/ + public byte byBigCarSpeedLimit;/*大车限速值,单位km/h*/ + public byte bySpeedCapEn; //是否启用超速抓拍,0-否,1-是 + public byte byCaptureCount;//抓拍张数1~5(正常) + public byte byRelatedIOOut; /*关联的IO输出口(可以同时关联多个),按位表示IO输出口,第0位表示IO输出口1,以此类推,0-不关联,1-关联*/ + public byte byFlashMode; /*闪光灯闪烁模式,0-同时闪,1-轮流闪*/ + public byte byEnableRadar; //是否启用雷达测速,0-否,1-是 + public byte byChangeLaneEnable; //违章变道抓拍使能,0-关闭,1-开启 + public byte byChangeLaneCapNo; //违章变道抓拍张数2-3 + public int dwCapTarget; //抓拍类型 bit0 表示机动车 bit1 表示非机动车 bit2 表示行人 0~表示不选择 1~表示选择 + public NET_ITC_INTERVAL_PARAM struInterval; //抓拍间隔参数 + public byte[] byRes3 = new byte[24]; + public NET_ITC_LANE_LOGIC_PARAM struLane; //车道属性,用byUseageType和byCarDriveDirect + public NET_ITC_LINE struLeftLaneLine; //左车道线,线类型为虚线、实线、单黄线和双黄线 + public NET_ITC_LINE struRightLaneLine; //右车道线,线类型为虚线、实线、单黄线和双黄线 + public NET_ITC_POLYGON struPlateRecog; //牌识区域参数 + public NET_ITC_POLYGON struTraceArea; //视频触发焦点区域 + public NET_VCA_LINE struForwardTrigLine; //正向触发线:一条线段,关心端点位置,目前只支持水平配置,接口按线段的两个端点保存。(一般配置为正向车辆的最佳触发位置) + public NET_VCA_LINE struBackwardTrigLine; //背向触发线:一条线段,关心端点位置,目前只支持水平配置,接口按线段的两个端点保存(一般配置为背向车辆的最佳触发位置) + public NET_VCA_LINE struLeftTrigLine; //左边触发线:一条线段,关心端点位置,目前只支持垂直配置,接口按线段的两个端点保存(一般配置为从左边进入车辆的最佳触发位置) + public NET_VCA_LINE struRightTrigLine; //右边触发线:一条线段,关心端点位置,目前只支持垂直配置,接口按线段的两个端点保存(一般配置为从右边进入车辆的最佳触发位置) + public byte[] byRes4 = new byte[60]; +} + +public static class NET_ITC_POST_HVT_PARAM extends Structure{ + public byte byLaneNum;//识别的车道个数,1-6 + public byte bySceneMode;//0-未知1-城区道路;2-小区出入口 + public byte byRoadExpBright;//路面期望亮度(视频曝光参数调整的依据之一。在无机动车时,依据此亮度期望值,调整视频曝光参数) + public byte byPlateExpBright;//车牌期望亮度(视频曝光参数调整的依据之一。在有机动车通过并识别到车牌时,依据此亮度期望值,对视频曝光参数调整) + public NET_ITC_POLYGON struDetectArea; //视频检测区域 + public NET_ITC_SNAPMODE_PARAM struCapMode = new NET_ITC_SNAPMODE_PARAM();//抓拍模式 + public NET_ITC_HVT_EC_PARAM struEcParam = new NET_ITC_HVT_EC_PARAM(); //曝光控制参数 + public NET_ITC_LANE_HVT_PARAM[] struLaneParam = new NET_ITC_LANE_HVT_PARAM[MAX_ITC_LANE_NUM]; //单车道属性 + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog = new NET_ITC_PLATE_RECOG_PARAM(); //牌识参数 + public NET_DVR_GEOGLOCATION struGeogLocation = new NET_DVR_GEOGLOCATION(); //地址位置(默认浙江) + public byte[] byRes = new byte[324]; +} + +//抓拍机4.0新增 +public static class NET_ITC_LANE_HVT_PARAM_V50 extends Structure{ + public byte byLaneNO; //关联的车道号1~255(用于叠加和上传) + public byte byFlashMode; //闪光灯闪烁模式,0-同时闪,1-轮流闪 + public byte bySignSpeed; //小车标志限高速,单位km/h + public byte bySpeedLimit; //小车限高速值,单位km/h + public byte bySignLowSpeed; //小车标志限底速,单位km/h + public byte byLowSpeedLimit; //小车限底速值,单位km/h + public byte byBigCarSignSpeed; //大车标志限高速,单位km/h(新交规) + public byte byBigCarSpeedLimit; //大车限高速值,单位km/h(新交规) + public byte byBigCarSignLowSpeed; //大车标志限底速,单位km/h + public byte byBigCarLowSpeedLimit; //大车限底速值,单位km/h + public byte bySnapTimes; //卡口抓拍张数,1~3 + public byte byDriveLineSnapTime;// 压线抓拍张数 1~3 + public byte byHighSpeedSnapTime;// 超高速抓拍张数1~3 + public byte byLowSpeedSnapTime;// 超低速抓拍张数1~3 + public byte byBanSnapTime;// 违反禁令抓拍张数 1~3 + public byte byReverseSnapTime;//逆行抓拍张数 1~3 + public byte byRelatedDriveWay; //关联车道号,用于匹配车检器 + public byte byLaneType; //车道类型,0-未配置、1-高速公路、2-城市快速路、0xff-其他道路 + //关联车道方向类型,参考ITC_RELA_LANE_DIRECTION_TYPE + //该参数为车道方向参数,与关联车道号byRelatedDriveWay对应,确保车道唯一性。 + public byte byRelaLaneDirectionType; + public byte[] byRes1 = new byte[27]; + public byte byChangeLaneEnable; //违章变道抓拍使能,0-关闭,1-开启 + public byte byChangeLaneCapNo; //违章变道抓拍张数2-3 + //目前仅使用第一个车道的,以后可能会扩展为多车道分别配置 + //类型, 按位表示,0-不启用,1-启用参考 ITC_VIOLATION_DETECT_TYPE + public int dwVioDetectType; + public int dwRelatedIOOut; //关联的IO输出口(可以同时关联多个),按位表示IO输出口,第0位表示IO输出口1,以此类推,0-不关联,1-关联 + public NET_ITC_LINE struTrigLine; //触发线,目前仅使用第一个车道的,以后可能会扩展为多车道分别配置 + public NET_ITC_LINE struLineLeft; //左车道线 + public NET_ITC_POLYGON struPlateRecog; //牌识区域 + public NET_ITC_LANE_LOGIC_PARAM struLane; //车道属性,用byUseageType和byCarDriveDirect + public NET_ITC_INTERVAL_PARAM struInterval;//抓拍间隔参数(20byte) + public byte[] byRes2 = new byte[280]; +} + +public static class NET_ITC_POST_HVT_PARAM_V50 extends Structure{ + public byte byLaneNum; //识别的车道个数,1-6 + public byte byCapType; //抓拍类型,0-机、非、人(默认),1-机动车 + public byte byCapMode; //抓拍方式,0-视频抽帧,1-打断抓拍,2-混合模式, + public byte bySecneMode; //场景模式,0-城区道路(默认),1-小区出入口,2-高速公路 + public byte bySpeedMode; //测速模式,0-无测速,1-雷达测速,2-视频测速 + public byte byLineRuleEffect; //触发规则线有效性,每一位代表一条触发线,0-无效;1-有效。bit0-左触发线;bit1-右触发线;bit2-视频检测区域 + public byte[] byRes1 = new byte[78]; + public NET_ITC_LINE struLeftTrigLine; //左触发线(一条垂直线) + public NET_ITC_LINE struRigtTrigLine; //右触发线(一条垂直线) + public NET_ITC_LINE struLaneBoundaryLine; //车道边界线(最右边车道的右车道线) + public NET_ITC_POLYGON struDetectArea; //视频检测区域 + public NET_DVR_GEOGLOCATION struGeogLocation; //地理位置(默认浙江省)计算时区 + public NET_ITC_LANE_HVT_PARAM_V50[] struLaneParam = new NET_ITC_LANE_HVT_PARAM_V50[MAX_ITC_LANE_NUM/*6*/]; //单车道属性 + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog; //牌识参数 + public byte[] byRes2 = new byte[260]; +} + + +public static class NET_ITC_LANE_PARAM extends Structure{ + public byte byEnable; //是否启用该车道,0-不启用,1-启用 + public byte byRelatedDriveWay;//关联的车道号 + public short wDistance; //线圈距离,计算速度 + public short wTrigDelayTime; //触发延迟时间(默认200),单位:毫秒 + public byte byTrigDelayDistance; //触发延迟距离(默认0),单位:分米 + public byte bySpeedCapEn; //是否启用超速抓拍,0-否,1-是 + public byte bySignSpeed;//标志限速,单位km/h + public byte bySpeedLimit;//限速值,单位km/h + public byte bySnapTimes; //抓拍次数(默认1),0-不抓拍,非0-连拍次数,最大5 + public byte byOverlayDriveWay; //OSD叠加的车道号 + public NET_ITC_INTERVAL_PARAM struInterval; //抓拍间隔参数 + public byte[] byRelatedIOOut = new byte[MAX_IOOUT_NUM]; //关联的IO输出口,可以同时关联多个 + public byte byFlashMode; //闪光灯闪烁模式,0-同时闪,1-轮流闪 + public byte byCartSignSpeed;//标志限速(大车),单位km/h + public byte byCartSpeedLimit;//限速值(大车),单位km/h + public byte byRelatedIOOutEx;//第0位表示IO输出口1,以此类推,0-不关联,1-关联 支持关联到8个(兼容byRelatedIOOut字段) + public NET_ITC_PLATE_RECOG_REGION_PARAM[] struPlateRecog = new NET_ITC_PLATE_RECOG_REGION_PARAM[MAX_LANEAREA_NUM]; //车道牌识参数 + public byte byLaneType; //车道类型,0-未配置、1-高速公路、2-城市快速路、0xff-其他道路 + public byte byUseageType; //车道用途类型,详见ITC_LANE_USEAGE_TYPE + //关联车道方向类型,参考ITC_RELA_LANE_DIRECTION_TYPE + //该参数为车道方向参数,与关联车道号对应,确保车道唯一性。 + public byte byRelaLaneDirectionType; + public byte byLowSpeedLimit; //小车限底速值,单位km/h + public byte byBigCarLowSpeedLimit; //大车限底速值,单位km/h + public byte byLowSpeedCapEn; //是否启用低速抓拍,0-否,1-是 + public byte byEmergencyCapEn; //是否启用应急车道抓拍,0-否,1-是 + public byte[] byRes = new byte[9]; +} + +//卡口RS485车检器触发参数 +public static class NET_ITC_POST_RS485_PARAM extends Structure{ + public byte byRelatedLaneNum;//关联的车道个数 + public byte byTriggerSpareMode; //触发备用模式,0-默认,1-卡口虚拟线圈模式,2-卡口混合车道模式 + public byte byFaultToleranceTime;//容错时间(单位:分钟),用于检测车检器是否正常的最大时间 + public byte byRes1; + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog = new NET_ITC_PLATE_RECOG_PARAM(); //牌识参数 + public NET_ITC_LANE_PARAM[] struLane = new NET_ITC_LANE_PARAM[MAX_ITC_LANE_NUM]; //关联的车道参数 + public byte[] byRes = new byte[32]; +} + +//卡口RS485雷达触发参数 +public static class NET_ITC_POST_RS485_RADAR_PARAM extends Structure{ + public byte byRelatedLaneNum;//关联的车道个数 + public byte[] byRes1 = new byte[3]; + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog = new NET_ITC_PLATE_RECOG_PARAM(); //牌识参数 + public NET_ITC_LANE_PARAM[] struLane = new NET_ITC_LANE_PARAM[MAX_ITC_LANE_NUM]; //关联的车道参数 + public NET_ITC_RADAR_PARAM struRadar = new NET_ITC_RADAR_PARAM(); //雷达参数 + public byte[] byRes = new byte[32]; +} + +public static class NET_ITC_TRIGGER_PARAM_UNION extends Union{ + public int[] uLen = new int[1070]; //参数 + public NET_ITC_POST_IOSPEED_PARAM struIOSpeed = new NET_ITC_POST_IOSPEED_PARAM(); //(卡口)IO测速参数 +// public NET_ITC_POST_SINGLEIO_PARAM struSingleIO = new NET_ITC_POST_SINGLEIO_PARAM(); //(卡口)单IO参数 + public NET_ITC_POST_RS485_PARAM struPostRs485 = new NET_ITC_POST_RS485_PARAM(); //(卡口)RS485车检器参数 + public NET_ITC_POST_RS485_RADAR_PARAM struPostRadar = new NET_ITC_POST_RS485_RADAR_PARAM(); //(卡口)RS485雷达参数 + public NET_ITC_POST_VTCOIL_PARAM struVtCoil = new NET_ITC_POST_VTCOIL_PARAM(); //(卡口)虚拟线圈参数 + public NET_ITC_POST_HVT_PARAM struHvt = new NET_ITC_POST_HVT_PARAM(); //(卡口)混行卡口参数 +// public NET_ITC_EPOLICE_IOTL_PARAM struIOTL = new NET_ITC_EPOLICE_IOTL_PARAM(); //(电警)IO红绿灯参数 +// public NET_ITC_EPOLICE_RS485_PARAM struEpoliceRs485 = new NET_ITC_EPOLICE_RS485_PARAM(); //(电警)RS485车检器触发参数 +// public NET_ITC_EPOLICE_RS485_PARAM struPERs485 = new NET_ITC_EPOLICE_RS485_PARAM(); //(卡式电警)RS485车检器触发参数 +// public NET_ITC_POST_MPR_PARAM struPostMpr = new NET_ITC_POST_MPR_PARAM(); //多帧检测触发(MPR) +// public NET_DVR_VIA_VTCOIL_PARAM struViaVtCoil = new NET_DVR_VIA_VTCOIL_PARAM(); //(VIA)视频检测参数 +// public NET_ITC_POST_IMT_PARAM struPostImt = new NET_ITC_POST_IMT_PARAM();//智慧监控触发 +// public NET_ITC_POST_PRS_PARAM struPostPrs = new NET_ITC_POST_PRS_PARAM();//视频检测触发 +// public NET_IPC_POST_HVT_PARAM struIpcHvt = new NET_IPC_POST_HVT_PARAM();//(IPC) 混行卡口参数 + public NET_ITC_POST_HVT_PARAM_V50 struHvtV50 = new NET_ITC_POST_HVT_PARAM_V50(); /*(卡口)混行卡口参数V50*/ +// public NET_ITC_POST_MOBILE_PARAM struPostMobile = new NET_ITC_POST_MOBILE_PARAM();// 移动交通触发模式 +// public NET_ITC_NOCOMITY_PEDESTRIAN_PARAM struNoComityPed = new NET_ITC_NOCOMITY_PEDESTRIAN_PARAM();//不礼让行人参数 +// public NET_ITC_REDLIGHT_PEDESTRIAN_PARAM struRedLightPed = new NET_ITC_REDLIGHT_PEDESTRIAN_PARAM();//行人闯红灯参数 +} + +//单个触发参数结构 +public static class NET_ITC_SINGLE_TRIGGERCFG extends Structure{ + public byte byEnable; //是否启用该触发模式,0-否,1-是 + public byte[] byRes1 = new byte[3]; + public int dwTriggerType; //触发类型,详见ITC_TRIGGERMODE_TYPE + public NET_ITC_TRIGGER_PARAM_UNION uTriggerParam = new NET_ITC_TRIGGER_PARAM_UNION(); //触发参数 + public byte[] byRes = new byte[64]; +} + +//触发参数结构 +public static class NET_ITC_TRIGGERCFG extends Structure{ + public int dwSize; //结构长度 + public NET_ITC_SINGLE_TRIGGERCFG struTriggerParam; //单个触发参数 + public byte[] byRes = new byte[32]; +} + +//单个IO接入信号灯参数 +public static class NET_ITC_SINGLE_IO_LIGHT_PARAM extends Structure{ + public byte byLightType; //交通灯导向类型,0-左转灯,1-直行灯,2-右转灯 + public byte byRelatedIO; //关联的IO口号 + public byte byRedLightState; //红灯电平状态,0-高电平红灯,1-低电平红灯 + public byte[] byRes = new byte[17]; +} + +//IO接入信号灯参数 +public static class NET_ITC_IO_LIGHT_PARAM extends Structure{ + public NET_ITC_SINGLE_IO_LIGHT_PARAM[] struIOLight = new NET_ITC_SINGLE_IO_LIGHT_PARAM[MAX_LIGHT_NUM]; //单个IO接入信号灯参数 + public byte[] byRes = new byte[8]; +} + +//单个485接入信号灯参数 +public static class NET_ITC_SINGLE_RS485_LIGHT_PARAM extends Structure{ + public byte byLightType; //交通灯导向类型,0-左转灯,1-直行灯,2-右转灯 + public byte byRelatedLightChan; //关联的红绿灯检测器通道号 + public byte byInputLight; //接入的信号灯类型,0-接红灯,1-接绿灯 + public byte byRelatedYLightChan; //关联的黄灯检测器通道号 + public byte[] byRes = new byte[16]; +} + +//485接入信号灯参数 +public static class NET_ITC_RS485_LIGHT_PARAM extends Structure{ + public NET_ITC_SINGLE_RS485_LIGHT_PARAM[] struRS485Light = new NET_ITC_SINGLE_RS485_LIGHT_PARAM[MAX_LIGHT_NUM]; //单个485接入信号灯参数 + public byte[] byRes = new byte[8]; +} + +public static class NET_POS_PARAM extends Structure{ + public short wLeft; + public short wTop; + public short wRight; + public short wBottom; +} + +//单组视频检测交通信号灯参数结构 +public static class NET_ITC_SINGLE_VIDEO_DETECT_LIGHT_PARAM extends Structure{ + public byte byLightNum; //交通灯个数 + public byte byStraightLight; //是否有直行标志灯,0-否 ,1-是 + public byte byLeftLight; //是否有左转标志灯,0-否,1-是 + public byte byRightLight; //是否有右转标志灯,0-否,1-是 + public byte byRedLight;//是否有红灯,0-否,1-是 + public byte byGreenLight; //是否有绿灯,0-否,1-是 + public byte byYellowLight; //是否有黄灯,0-否,1-是 + public byte byYellowLightTime;//取值范围(0~10s)(ITC3.7Ver) + public NET_POS_PARAM struLightRect; //交通灯区域 + public byte[] byRes = new byte[24]; +} + +//视频检测交通信号灯参数结构(最大可有12个区域检测,488字节) +public static class NET_ITC_VIDEO_DETECT_LIGHT_PARAM extends Structure{ + public NET_ITC_SINGLE_VIDEO_DETECT_LIGHT_PARAM[] struTrafficLight = new NET_ITC_SINGLE_VIDEO_DETECT_LIGHT_PARAM[MAX_VIDEO_DETECT_LIGHT_NUM]; //单个视频检测信号灯参数 + public byte[] byRes = new byte[8]; +} + +//交通信号灯接入参数 +public static class NET_ITC_LIGHT_ACCESSPARAM_UNION extends Union{ + public int[] uLen = new int[122]; + public NET_ITC_IO_LIGHT_PARAM struIOLight; //IO接入信号灯参数 + public NET_ITC_RS485_LIGHT_PARAM struRS485Light; //485接入信号灯参数 + public NET_ITC_VIDEO_DETECT_LIGHT_PARAM struVideoDelectLight; //视频检测信号灯参数 +} + +//交通信号灯参数结构 +public static class NET_ITC_TRAFFIC_LIGHT_PARAM extends Structure{ + public byte bySource; //交通信号灯接入源,0-IO接入,1-RS485接入 + public byte[] byRes1 = new byte[3]; + public NET_ITC_LIGHT_ACCESSPARAM_UNION struLightAccess = new NET_ITC_LIGHT_ACCESSPARAM_UNION();//信号灯接入参数 + public byte[] byRes = new byte[32]; +} + +//违规检测参数结构 +public static class NET_ITC_VIOLATION_DETECT_PARAM extends Structure{ + public int dwVioDetectType; //违规检测类型, 按位表示, 详见ITC_VIOLATION_DETECT_TYPE ,0-不启用,1-启用 + public byte byDriveLineSnapTimes; //压车道线抓拍张数,2-3 + public byte byReverseSnapTimes; //逆行抓拍,2-3 + public short wStayTime; //机占非停留时间(该时间后抓拍),单位s + public byte byNonDriveSnapTimes;//机占非抓拍张数2-3 + public byte byChangeLaneTimes;//违法变道抓拍张数 2-3 + public byte bybanTimes;//违法禁令抓拍张数2-3 + public byte byDriveLineSnapSen;// 压线灵敏度(0~100)(3.7Ver) + public short wSnapPosFixPixel; //第2,3张抓拍位置最小偏移(违反信号灯时起效)(单位:像素) 命名需改进 + public byte bySpeedTimes;//违法超速抓拍张数2-3(3.8Ver) + public byte byTurnAroundEnable;//违章掉头使能 0~关闭 1~开启 + public byte byThirdPlateRecogTime;//第三张牌识时间 0~180s + public byte byPostSnapTimes;//卡口抓拍张数,1-2张 + public byte[] byRes1 = new byte[18]; + public short wStopLineDis; //电警第2张违规图片与停止线的最短距离,[0,300]单位(像素) + public byte[] byRes = new byte[14]; +} + +//违规检测线参数结构 +public static class NET_ITC_VIOLATION_DETECT_LINE extends Structure{ + public NET_ITC_LINE struLaneLine = new NET_ITC_LINE(); //车道线参数 + public NET_ITC_LINE struStopLine = new NET_ITC_LINE(); //停止线参数 + public NET_ITC_LINE struRedLightLine = new NET_ITC_LINE(); //闯红灯触发线参数 + public NET_ITC_LINE struCancelLine = new NET_ITC_LINE(); //直行触发位置取消线 + public NET_ITC_LINE struWaitLine = new NET_ITC_LINE(); //待行区停止线参数 + public NET_ITC_LINE[] struRes = new NET_ITC_LINE[8]; +} + +//单个车道视频电警触发参数结构 +public static class NET_ITC_LANE_VIDEO_EPOLICE_PARAM extends Structure{ + public byte byLaneNO; //关联的车道号 + public byte bySensitivity; //线圈灵敏度,[1,100] + public byte byEnableRadar;//启用雷达测试0-不启用,1-启用 + //关联车道方向类型,参考ITC_RELA_LANE_DIRECTION_TYPE + //该参数为车道方向参数,与关联车道号对应,确保车道唯一性。 + public byte byRelaLaneDirectionType; + public NET_ITC_LANE_LOGIC_PARAM struLane; //车道参数 + public NET_ITC_VIOLATION_DETECT_PARAM struVioDetect; //违规检测参数 + public NET_ITC_VIOLATION_DETECT_LINE struLine; //违规检测线 + public NET_ITC_POLYGON struPlateRecog; //牌识区域参数 + public byte byRecordEnable;//闯红灯周期录像标志,0-不录像,1-录像 + public byte byRecordType;//闯红灯录像类型,0-预录,1-延时录像 + public byte byPreRecordTime;//闯红灯录像片段预录时间(默认0),单位:秒 + public byte byRecordDelayTime;//闯红灯录像片段延时时间(默认0),单位:秒 + public byte byRecordTimeOut;//闯红灯周期录像超时时间(秒) + public byte byCarSpeedLimit; //车速限制值,单位km/h + public byte byCarSignSpeed;//标志限速,单位km/h + public byte bySnapPicPreRecord; //抓拍图片预录时间点;0-默认值(第二张图片),1-第一张图片,2-第二张图片,3-第三张图片 + public NET_ITC_INTERVAL_PARAM struInterval;//抓拍间隔参数(20byte) + public byte[] byRes = new byte[36]; +} + +//视频电警触发参数结构 +public static class NET_ITC_VIDEO_EPOLICE_PARAM extends Structure{ + public byte byEnable; //是否启用,0-不启用,1-启用 + public byte byLaneNum; //识别的车道个数 + public byte byLogicJudge;//闯红灯违规判断逻辑,设置值为:0-按方向,1-按车道 + public byte byRes1; + public NET_ITC_PLATE_RECOG_PARAM struPlateRecog; //牌识参数 + public NET_ITC_TRAFFIC_LIGHT_PARAM struTrafficLight; //交通信号灯参数 + public NET_ITC_LANE_VIDEO_EPOLICE_PARAM[] struLaneParam = new NET_ITC_LANE_VIDEO_EPOLICE_PARAM[MAX_ITC_LANE_NUM]; //单车道参数 + public NET_ITC_LINE struLaneBoundaryLine; //车道边界线(最右边车道的边界线) + public NET_ITC_LINE struLeftLine; //左转弯分界线 + public NET_ITC_LINE struRightLine; //右转弯分界线 + public NET_ITC_LINE struTopZebraLine; //上部斑马线 + public NET_ITC_LINE struBotZebraLine; //下部斑马线 + public byte[] byRes = new byte[32]; +} + +public static class NET_DVR_CURTRIGGERMODE extends Structure{ + public int dwSize; + public int dwTriggerType; //触发类型,详见ITC_TRIGGERMODE_TYPE + public byte[] byRes = new byte[24]; +} + +public static class NET_ITC_VIDEO_TRIGGER_COND extends Structure{ + public int dwSize; + public int dwChannel; + public int dwTriggerMode; //视频触发模式类型,详见ITC_TRIGGERMODE_TYPE + public byte[] byRes = new byte[16]; +} + +public static class NET_ITC_VIDEO_TRIGGER_PARAM_UNION extends Union{ + public int[] uLen = new int[1150]; + public NET_ITC_VIDEO_EPOLICE_PARAM struVideoEP = new NET_ITC_VIDEO_EPOLICE_PARAM(); //视频电警参数 +} + +public static class NET_ITC_VIDEO_TRIGGER_PARAM extends Structure{ + public int dwSize; + public int dwMode; //触发模式,详见ITC_TRIGGERMODE_TYPE + public NET_ITC_VIDEO_TRIGGER_PARAM_UNION uVideoTrigger = new NET_ITC_VIDEO_TRIGGER_PARAM_UNION(); //触发模式参数 + public byte[] byRes = new byte[32]; +} + +public static class NET_DVR_CMS_PARAM extends Structure{ + public int dwSize; + public NET_DVR_IPADDR struAddr = new NET_DVR_IPADDR(); // 平台服务器IP + public short wServerPort; // 平台服务器侦听端口, + public byte bySeverProtocolType; //平台协议类型 1-私有,2-Ehome + public byte byStatus; //设备注册到该平台的状态,1-未注册,2-已注册 + public byte[] sDeviceId = new byte[NAME_LEN/*32*/]; //设备ID,由平台提供 + public byte[] sPassWord = new byte[PASSWD_LEN]; //密码 + /********* IPC5.1.7 新增参数 Begin 2014-03-21***********/ + public byte[] sPlatformEhomeVersion = new byte[NAME_LEN];//平台EHOME协议版本 + /********* IPC5.1.7 新增参数 end 2014-03-21***********/ + public byte byNetWork; //网络类型:0- 无意义,1-自动,2-有线网络优先,3-有线网络,4-3G网络(无线网络),5-有线网络1,6-有线网络2 + public byte byAddressType; //0 - 无意义, 1 - ipv4/ipv6地址,2 - 域名 + public byte byProtocolVersion; //协议版本 0 - 无意义, 1 – v2.0,2 – v4.0,3-v2.6 + public byte byRes1; + public byte[] sDomainName= new byte[MAX_DOMAIN_NAME/*64*/]; //平台服务器域名,byAddressType为2时有效 + public byte byEnable; //0-关闭,1-开启 + public byte[] byRes= new byte[139]; // 保留字节 +} + +//设置完全获取出厂值 +public static class NET_DVR_COMPLETE_RESTORE_INFO extends Structure{ + public int dwSize ; //结构体长度 + public int dwChannel; //通道号 + public byte[] byRes= new byte[64]; +} + +public static class NET_DVR_STD_ABILITY extends Structure { + public Pointer lpCondBuffer; //[in]条件参数(码字格式),例如通道号等.可以为NULL + public int dwCondSize; //[in] dwCondSize指向的内存大小 + public Pointer lpOutBuffer; //[out]输出参数(XML格式),不为NULL + public int dwOutSize; //[in] lpOutBuffer指向的内存大小 + public Pointer lpStatusBuffer; //[out]返回的状态参数(XML格式),获取成功时不会赋值,如果不需要,可以置NULL + public int dwStatusSize; //[in] lpStatusBuffer指向的内存大小 + public int dwRetSize; //[out]获取到的数据长度(lpOutBuffer或者lpStatusBuffer指向的实际数据长度) + public byte[] byRes = new byte[32]; //保留字节 +} + +public static class NET_DVR_STD_CONFIG extends Structure { + public Pointer lpCondBuffer; //[in]条件参数(结构体格式),例如通道号等.可以为NULL + public int dwCondSize; //[in] lpCondBuffer指向的内存大小 + public Pointer lpInBuffer; //[in]输入参数(结构体格式),设置时不为NULL,获取时为NULL + public int dwInSize; //[in] lpInBuffer指向的内存大小 + public Pointer lpOutBuffer; //[out]输出参数(结构体格式),获取时不为NULL,设置时为NULL + public int dwOutSize; //[in] lpOutBuffer指向的内存大小 + public Pointer lpStatusBuffer; //[out]返回的状态参数(XML格式),获取成功时不会赋值,如果不需要,可以置NULL + public int dwStatusSize; //[in] lpStatusBuffer指向的内存大小 + public Pointer lpXmlBuffer; //[in/out]byDataType = 1时有效,xml格式数据 + public int dwXmlSize; //[in/out]lpXmlBuffer指向的内存大小,获取时同时作为输入和输出参数,获取成功后会修改会实际长度,设置时表示实际长度,而不是整个内存大小 + public byte byDataType; //[in]输入/输出参数类型,0-使用结构体类型lpInBuffer/lpOutBuffer有效,1-使用XML类型lpXmlBuffer有效 + public byte[] byRes= new byte[23]; +} + +public static final int NET_SDK_MAX_FILE_PATH = 256;//路径长度 + +public static class NET_DVR_LOCAL_SDK_PATH extends Structure { + public byte[] sPath = new byte[NET_SDK_MAX_FILE_PATH];//组件库地址 + public byte[] byRes = new byte[128]; +} + +public static class BYTE_ARRAY extends Structure +{ + public byte[] byValue; + + public BYTE_ARRAY(int iLen) { + byValue = new byte[iLen]; + } +} + +public static class NET_DVR_JSON_DATA_CFG extends Structure { + public int dwSize; //结构体大小 + public Pointer lpJsonData; //JSON报文 + public int dwJsonDataSize; //JSON报文大小 + public Pointer lpPicData; //图片内容 + public int dwPicDataSize; //图片内容大小 + public int lpInfraredFacePicBuffer; //红外人脸图片数据缓存 + public Pointer dwInfraredFacePicSize; //红外人脸图片数据大小,等于0时,代表无人脸图片数据(当JSON报文为当ResponseStatus(JSON)报文时,该字段无意义;当Inbound Data(JSON)报文中没有infraredFaceURL时,该字段需要带上二进制图片内容) + public byte[] byRes = new byte[248]; +} + +public static class Callback_USER extends Structure { + public byte[] byDeviceID= new byte[16]; + public byte[] byCardNo= new byte[32]; + public byte[] byDevIP= new byte[16]; +} + + + /***API函数声明,详细说明见API手册***/ +//回调函数 + public static interface FRealDataCallback_V30 extends Callback { + public void invoke(int lRealHandle, int dwDataType, + ByteByReference pBuffer, int dwBufSize, Pointer pUser); + } + + public static interface FMSGCallBack extends Callback { + public void invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser); + } + + public static interface FMSGCallBack_V31 extends Callback { + public boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) throws UnsupportedEncodingException; + } + + public static interface FMessCallback extends Callback { + public boolean invoke(int lCommand,String sDVRIP,String pBuf,int dwBufLen); + } + + public static interface FMessCallback_EX extends Callback { + public boolean invoke(int lCommand,int lUserID,String pBuf,int dwBufLen); + } + + public static interface FMessCallback_NEW extends Callback { + public boolean invoke(int lCommand,String sDVRIP,String pBuf,int dwBufLen, short dwLinkDVRPort); + } + + public static interface FMessageCallback extends Callback { + public boolean invoke(int lCommand,String sDVRIP,String pBuf,int dwBufLen, int dwUser); + } + + public static interface FExceptionCallback extends Callback { + public void invoke(int dwType, int lUserID, int lHandle, Pointer pUser); + } + public static interface FDrawFun extends Callback { + public void invoke(int lRealHandle,W32API.HDC hDc,int dwUser); + } + + public static interface FStdDataCallback extends Callback { + public void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer,int dwBufSize,int dwUser); + } + + public static interface FPlayDataCallback extends Callback { + public void invoke(int lPlayHandle, int dwDataType, ByteByReference pBuffer,int dwBufSize,int dwUser); + } + + public static interface FVoiceDataCallback extends Callback { + public void invoke(int lVoiceComHandle, Pointer pRecvDataBuffer, int dwBufSize, byte byAudioFlag, int dwUser); + } + + public static interface FVoiceDataCallback_V30 extends Callback { + public void invoke(NativeLong lVoiceComHandle, Pointer pRecvDataBuffer, int dwBufSize, byte byAudioFlag,Pointer pUser); + } + + public static interface FVoiceDataCallback_MR extends Callback { + public void invoke(int lVoiceComHandle, Pointer pRecvDataBuffer, int dwBufSize, byte byAudioFlag, int dwUser); + } + + public static interface FVoiceDataCallback_MR_V30 extends Callback { + public void invoke(int lVoiceComHandle, Pointer pRecvDataBuffer, int dwBufSize, byte byAudioFlag, Pointer pUser); + } + + public static interface FVoiceDataCallback2 extends Callback { + public void invoke(String pRecvDataBuffer, int dwBufSize, Pointer pUser); + } + + public static interface FSerialDataCallback extends Callback { + public void invoke(int lSerialHandle,String pRecvDataBuffer,int dwBufSize,int dwUser); + } + + public static interface FRowDataCallback extends Callback { + public void invoke(int lUserID,String sIPAddr, int lRowAmout, String pRecvDataBuffer,int dwBufSize,int dwUser); + } + + public static interface FColLocalDataCallback extends Callback { + public void invoke(int lUserID, String sIPAddr, int lColumnAmout, String pRecvDataBuffer,int dwBufSize,int dwUser); + } + + public static interface FColGlobalDataCallback extends Callback { + public void invoke(int lUserID, String sIPAddr, int lColumnAmout, String pRecvDataBuffer,int dwBufSize,int dwUser); + } + + public static interface FJpegdataCallback extends Callback { + public int invoke(int lCommand, int lUserID, String sDVRIP, String sJpegName, String pJpegBuf,int dwBufLen, int dwUser); + } + + public static interface FPostMessageCallback extends Callback { + public int invoke(int dwType, int lIndex); + } + + //接口定义 + boolean NET_DVR_Init(); + boolean NET_DVR_Cleanup(); + + boolean NET_DVR_SetSDKInitCfg(int enumType, Pointer lpInBuff); + + boolean NET_DVR_SetSDKLocalCfg(int enumType, Pointer lpInBuff); + boolean NET_DVR_GetSDKLocalCfg(int enumType, Pointer lpOutBuff); + + boolean NET_DVR_SetDVRMessage(int nMessage,int hWnd); +//NET_DVR_SetDVRMessage的扩展 + boolean NET_DVR_SetExceptionCallBack_V30(int nMessage, int hWnd, FExceptionCallback fExceptionCallback, Pointer pUser); + + boolean NET_DVR_SetDVRMessCallBack(FMessCallback fMessCallback); + boolean NET_DVR_SetDVRMessCallBack_EX(FMessCallback_EX fMessCallback_EX); + boolean NET_DVR_SetDVRMessCallBack_NEW(FMessCallback_NEW fMessCallback_NEW); + boolean NET_DVR_SetDVRMessageCallBack(FMessageCallback fMessageCallback, int dwUser); + + boolean NET_DVR_SetDVRMessageCallBack_V30(FMSGCallBack fMessageCallback, Pointer pUser); + boolean NET_DVR_SetDVRMessageCallBack_V31(FMSGCallBack_V31 fMessageCallback, Pointer pUser); + + boolean NET_DVR_SetConnectTime(int dwWaitTime, int dwTryTimes ); + boolean NET_DVR_SetReconnect(int dwInterval, boolean bEnableRecon ); + int NET_DVR_GetSDKVersion(); + int NET_DVR_GetSDKBuildVersion(); + int NET_DVR_IsSupport(); + boolean NET_DVR_StartListen(String sLocalIP,short wLocalPort); + boolean NET_DVR_StopListen(); + + + int NET_DVR_StartListen_V30(String sLocalIP, short wLocalPort, FMSGCallBack DataCallback , Pointer pUserData ); + boolean NET_DVR_StopListen_V30(int lListenHandle); + int NET_DVR_Login(String sDVRIP,short wDVRPort,String sUserName,String sPassword,NET_DVR_DEVICEINFO lpDeviceInfo); + int NET_DVR_Login_V30(String sDVRIP, short wDVRPort, String sUserName, String sPassword, NET_DVR_DEVICEINFO_V30 lpDeviceInfo); + int NET_DVR_Login_V40(NET_DVR_USER_LOGIN_INFO pLoginInfo,NET_DVR_DEVICEINFO_V40 lpDeviceInfo); + boolean NET_DVR_Logout(int lUserID); + boolean NET_DVR_Logout_V30(int lUserID); + int NET_DVR_GetLastError(); + String NET_DVR_GetErrorMsg(IntByReference pErrorNo); + boolean NET_DVR_SetShowMode(int dwShowType,int colorKey); + boolean NET_DVR_GetDVRIPByResolveSvr(String sServerIP, short wServerPort, String sDVRName,short wDVRNameLen,String sDVRSerialNumber,short wDVRSerialLen,String sGetIP); + boolean NET_DVR_GetDVRIPByResolveSvr_EX(String sServerIP, short wServerPort, String sDVRName, short wDVRNameLen, String sDVRSerialNumber, short wDVRSerialLen,String sGetIP, IntByReference dwPort); + +//预览相关接口 + int NET_DVR_RealPlay(int lUserID,NET_DVR_CLIENTINFO lpClientInfo); + int NET_DVR_RealPlay_V30(int lUserID, NET_DVR_CLIENTINFO lpClientInfo, FRealDataCallback_V30 fRealDataCallback_V30, Pointer pUser , boolean bBlocked ); + int NET_DVR_RealPlay_V40(int lUserID, NET_DVR_PREVIEWINFO lpPreviewInfo, FRealDataCallback_V30 fRealDataCallback_V30, Pointer pUser); + boolean NET_DVR_StopRealPlay(int lRealHandle); + boolean NET_DVR_RigisterDrawFun(int lRealHandle,FDrawFun fDrawFun,int dwUser); + boolean NET_DVR_SetPlayerBufNumber(int lRealHandle,int dwBufNum); + boolean NET_DVR_ThrowBFrame(int lRealHandle,int dwNum); + boolean NET_DVR_SetAudioMode(int dwMode); + boolean NET_DVR_OpenSound(int lRealHandle); + boolean NET_DVR_CloseSound(); + boolean NET_DVR_OpenSoundShare(int lRealHandle); + boolean NET_DVR_CloseSoundShare(int lRealHandle); + boolean NET_DVR_Volume(int lRealHandle,short wVolume); + boolean NET_DVR_SaveRealData(int lRealHandle,String sFileName); + boolean NET_DVR_StopSaveRealData(int lRealHandle); + boolean NET_DVR_SetRealDataCallback(int lRealHandle,FRowDataCallback fRealDataCallback,int dwUser); + boolean NET_DVR_SetStandardDataCallback(int lRealHandle,FStdDataCallback fStdDataCallback,int dwUser); + boolean NET_DVR_CapturePicture(int lRealHandle,String sPicFileName);//bmp + +//动态生成I帧 + boolean NET_DVR_MakeKeyFrame(int lUserID, int lChannel);//主码流 + boolean NET_DVR_MakeKeyFrameSub(int lUserID, int lChannel);//子码流 + +//云台控制相关接口 + boolean NET_DVR_PTZControl(int lRealHandle,int dwPTZCommand,int dwStop); + boolean NET_DVR_PTZControl_Other(int lUserID,int lChannel,int dwPTZCommand,int dwStop); + boolean NET_DVR_TransPTZ(int lRealHandle,String pPTZCodeBuf,int dwBufSize); + boolean NET_DVR_TransPTZ_Other(int lUserID,int lChannel,String pPTZCodeBuf,int dwBufSize); + boolean NET_DVR_PTZPreset(int lRealHandle,int dwPTZPresetCmd,int dwPresetIndex); + boolean NET_DVR_PTZPreset_Other(int lUserID,int lChannel,int dwPTZPresetCmd,int dwPresetIndex); + boolean NET_DVR_TransPTZ_EX(int lRealHandle,String pPTZCodeBuf,int dwBufSize); + boolean NET_DVR_PTZControl_EX(int lRealHandle,int dwPTZCommand,int dwStop); + boolean NET_DVR_PTZPreset_EX(int lRealHandle,int dwPTZPresetCmd,int dwPresetIndex); + boolean NET_DVR_PTZCruise(int lRealHandle,int dwPTZCruiseCmd,byte byCruiseRoute, byte byCruisePoint, short wInput); + boolean NET_DVR_PTZCruise_Other(int lUserID,int lChannel,int dwPTZCruiseCmd,byte byCruiseRoute, byte byCruisePoint, short wInput); + boolean NET_DVR_PTZCruise_EX(int lRealHandle,int dwPTZCruiseCmd,byte byCruiseRoute, byte byCruisePoint, short wInput); + boolean NET_DVR_PTZTrack(int lRealHandle, int dwPTZTrackCmd); + boolean NET_DVR_PTZTrack_Other(int lUserID, int lChannel, int dwPTZTrackCmd); + boolean NET_DVR_PTZTrack_EX(int lRealHandle, int dwPTZTrackCmd); + boolean NET_DVR_PTZControlWithSpeed(int lRealHandle, int dwPTZCommand, int dwStop, int dwSpeed); + boolean NET_DVR_PTZControlWithSpeed_Other(int lUserID, int lChannel, int dwPTZCommand, int dwStop, int dwSpeed); + boolean NET_DVR_PTZControlWithSpeed_EX(int lRealHandle, int dwPTZCommand, int dwStop, int dwSpeed); + boolean NET_DVR_GetPTZCruise(int lUserID,int lChannel,int lCruiseRoute, NET_DVR_CRUISE_RET lpCruiseRet); + boolean NET_DVR_PTZMltTrack(int lRealHandle,int dwPTZTrackCmd, int dwTrackIndex); + boolean NET_DVR_PTZMltTrack_Other(int lUserID,int lChannel,int dwPTZTrackCmd, int dwTrackIndex); + boolean NET_DVR_PTZMltTrack_EX(int lRealHandle,int dwPTZTrackCmd, int dwTrackIndex); + +//文件查找与回放 + int NET_DVR_FindFile(int lUserID,int lChannel,int dwFileType, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime); + int NET_DVR_FindNextFile(int lFindHandle,NET_DVR_FIND_DATA lpFindData); + boolean NET_DVR_FindClose(int lFindHandle); + int NET_DVR_FindNextFile_V30(int lFindHandle, NET_DVR_FINDDATA_V30 lpFindData); + int NET_DVR_FindFile_V30(int lUserID, NET_DVR_FILECOND pFindCond); + boolean NET_DVR_FindClose_V30(int lFindHandle); + int NET_DVR_FindPicture(int lUserID, NET_DVR_FIND_PICTURE_PARAM pFindParam); +//2007-04-16增加查询结果带卡号的文件查找 + int NET_DVR_FindNextFile_Card(int lFindHandle, NET_DVR_FINDDATA_CARD lpFindData); + int NET_DVR_FindFile_Card(int lUserID, int lChannel, int dwFileType, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime); + boolean NET_DVR_LockFileByName(int lUserID, String sLockFileName); + boolean NET_DVR_UnlockFileByName(int lUserID, String sUnlockFileName); +// int NET_DVR_PlayBackByName(int lUserID,String sPlayBackFileName, HWND hWnd); +// int NET_DVR_PlayBackByTime(int lUserID,int lChannel, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime, HWND hWnd); + int NET_DVR_PlayBackByTime_V40(int lUserID, NET_DVR_VOD_PARA pVodPara); + boolean NET_DVR_PlayBackControl(int lPlayHandle,int dwControlCode,int dwInValue,IntByReference LPOutValue); + boolean NET_DVR_PlayBackControl_V40(int lPlayHandle,int dwControlCode, Pointer lpInBuffer, int dwInLen, Pointer lpOutBuffer, IntByReference lpOutLen); + boolean NET_DVR_StopPlayBack(int lPlayHandle); + boolean NET_DVR_SetPlayDataCallback(int lPlayHandle,FPlayDataCallback fPlayDataCallback,int dwUser); + boolean NET_DVR_PlayBackSaveData(int lPlayHandle,String sFileName); + boolean NET_DVR_StopPlayBackSave(int lPlayHandle); + boolean NET_DVR_GetPlayBackOsdTime(int lPlayHandle, NET_DVR_TIME lpOsdTime); + boolean NET_DVR_PlayBackCaptureFile(int lPlayHandle,String sFileName); + int NET_DVR_GetFileByName(int lUserID,String sDVRFileName,String sSavedFileName); + int NET_DVR_GetFileByTime(int lUserID,int lChannel, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime, String sSavedFileName); + int NET_DVR_GetFileByTime_V40(int lUserID, String sSavedFileName, NET_DVR_PLAYCOND pDownloadCond); + boolean NET_DVR_StopGetFile(int lFileHandle); + int NET_DVR_GetDownloadPos(int lFileHandle); + int NET_DVR_GetPlayBackPos(int lPlayHandle); + +//升级 + int NET_DVR_Upgrade(int lUserID, String sFileName); + int NET_DVR_GetUpgradeState(int lUpgradeHandle); + int NET_DVR_GetUpgradeProgress(int lUpgradeHandle); + boolean NET_DVR_CloseUpgradeHandle(int lUpgradeHandle); + boolean NET_DVR_SetNetworkEnvironment(int dwEnvironmentLevel); +//远程格式化硬盘 + int NET_DVR_FormatDisk(int lUserID,int lDiskNumber); + boolean NET_DVR_GetFormatProgress(int lFormatHandle, IntByReference pCurrentFormatDisk,IntByReference pCurrentDiskPos,IntByReference pFormatStatic); + boolean NET_DVR_CloseFormatHandle(int lFormatHandle); +//报警 + int NET_DVR_SetupAlarmChan(int lUserID); + boolean NET_DVR_CloseAlarmChan(int lAlarmHandle); + int NET_DVR_SetupAlarmChan_V30(int lUserID); + int NET_DVR_SetupAlarmChan_V41(int lUserID, NET_DVR_SETUPALARM_PARAM lpSetupParam); + boolean NET_DVR_CloseAlarmChan_V30(int lAlarmHandle); +//语音对讲 + int NET_DVR_StartVoiceCom(int lUserID, FVoiceDataCallback fVoiceDataCallback, int dwUser); + NativeLong NET_DVR_StartVoiceCom_V30(int lUserID, int dwVoiceChan, boolean bNeedCBNoEncData, FVoiceDataCallback_V30 fVoiceDataCallback, Pointer pUser); + boolean NET_DVR_SetVoiceComClientVolume(int lVoiceComHandle, short wVolume); + boolean NET_DVR_StopVoiceCom(NativeLong lVoiceComHandle); +//语音转发 + int NET_DVR_StartVoiceCom_MR(int lUserID, FVoiceDataCallback_MR fVoiceDataCallback, int dwUser); + int NET_DVR_StartVoiceCom_MR_V30(int lUserID, int dwVoiceChan, FVoiceDataCallback_MR_V30 fVoiceDataCallback, Pointer pUser); + boolean NET_DVR_VoiceComSendData(int lVoiceComHandle, byte[] pSendBuf, int dwBufSize); + +//语音广播 + boolean NET_DVR_ClientAudioStart(); + boolean NET_DVR_ClientAudioStart_V30(FVoiceDataCallback2 fVoiceDataCallback2, Pointer pUser); + boolean NET_DVR_ClientAudioStop(); + boolean NET_DVR_AddDVR(int lUserID); + int NET_DVR_AddDVR_V30(int lUserID, int dwVoiceChan); + boolean NET_DVR_DelDVR(int lUserID); + boolean NET_DVR_DelDVR_V30(int lVoiceHandle); +//////////////////////////////////////////////////////////// +//透明通道设置 + int NET_DVR_SerialStart(int lUserID,int lSerialPort,FSerialDataCallback fSerialDataCallback,int dwUser); + +public static interface FSerialDataCallback_V40 extends Callback { + public void invoke(int lSerialHandle, int lCHannel, byte[] pRecvDataBuffer, int dwBufSize, Pointer pUser); +} + int NET_DVR_SerialStart_V40(int lUserID, Pointer lpInBuffer, int dwInBufferSize, FSerialDataCallback_V40 fSerialDataCallback_V40,Pointer pUser); + +//485作为透明通道时,需要指明通道号,因为不同通道号485的设置可以不同(比如波特率) + boolean NET_DVR_SerialSend(int lSerialHandle, int lChannel, byte[] pSendBuf,int dwBufSize); + boolean NET_DVR_SerialStop(int lSerialHandle); + boolean NET_DVR_SendTo232Port(int lUserID, String pSendBuf, int dwBufSize); + boolean NET_DVR_SendToSerialPort(int lUserID, int dwSerialPort, int dwSerialIndex, String pSendBuf, int dwBufSize); + + //Win64、Linux32、Linux64 + Pointer NET_DVR_InitG722Encoder(NET_DVR_AUDIOENC_INFO enc_info); + boolean NET_DVR_EncodeG722Frame(Pointer handle, NET_DVR_AUDIOENC_PROCESS_PARAM param); + void NET_DVR_ReleaseG722Encoder(Pointer pEncodeHandle); + + Pointer NET_DVR_InitG722Decoder(); + boolean NET_DVR_DecodeG722Frame(Pointer handle, NET_DVR_AUDIODEC_PROCESS_PARAM param); + void NET_DVR_ReleaseG722Decoder(Pointer pDecHandle); + + //G711: Win64、Linux32、Linux64 + Pointer NET_DVR_InitG711Encoder(Pointer enc_info); //NET_DVR_AUDIOENC_INFO + boolean NET_DVR_EncodeG711Frame(Pointer handle, NET_DVR_AUDIOENC_PROCESS_PARAM p_enc_proc_param); + boolean NET_DVR_ReleaseG711Encoder(Pointer pEncodeHandle); + + Pointer NET_DVR_InitG711Decoder(); + boolean NET_DVR_DecodeG711Frame(Pointer handle, NET_DVR_AUDIODEC_PROCESS_PARAM p_dec_proc_param); + boolean NET_DVR_ReleaseG711Decoder(Pointer pDecHandle); + +//远程控制本地显示 + boolean NET_DVR_ClickKey(int lUserID, int lKeyIndex); +//远程控制设备端手动录像 + boolean NET_DVR_StartDVRRecord(int lUserID,int lChannel,int lRecordType); + boolean NET_DVR_StopDVRRecord(int lUserID,int lChannel); +//解码卡 + boolean NET_DVR_InitDevice_Card(IntByReference pDeviceTotalChan); + boolean NET_DVR_ReleaseDevice_Card(); + boolean NET_DVR_InitDDraw_Card(int hParent,int colorKey); + boolean NET_DVR_ReleaseDDraw_Card(); + int NET_DVR_RealPlay_Card(int lUserID,NET_DVR_CARDINFO lpCardInfo,int lChannelNum); + boolean NET_DVR_ResetPara_Card(int lRealHandle,NET_DVR_DISPLAY_PARA lpDisplayPara); + boolean NET_DVR_RefreshSurface_Card(); + boolean NET_DVR_ClearSurface_Card(); + boolean NET_DVR_RestoreSurface_Card(); + boolean NET_DVR_OpenSound_Card(int lRealHandle); + boolean NET_DVR_CloseSound_Card(int lRealHandle); + boolean NET_DVR_SetVolume_Card(int lRealHandle,short wVolume); + boolean NET_DVR_AudioPreview_Card(int lRealHandle,boolean bEnable); + int NET_DVR_GetCardLastError_Card(); + Pointer NET_DVR_GetChanHandle_Card(int lRealHandle); + boolean NET_DVR_CapturePicture_Card(int lRealHandle, String sPicFileName); +//获取解码卡序列号此接口无效,改用GetBoardDetail接口获得(2005-12-08支持) + boolean NET_DVR_GetSerialNum_Card(int lChannelNum,IntByReference pDeviceSerialNo); +//日志 + int NET_DVR_FindDVRLog(int lUserID, int lSelectMode, int dwMajorType,int dwMinorType, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime); + int NET_DVR_FindNextLog(int lLogHandle, NET_DVR_LOG lpLogData); + boolean NET_DVR_FindLogClose(int lLogHandle); + int NET_DVR_FindDVRLog_V30(int lUserID, int lSelectMode, int dwMajorType,int dwMinorType, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime, boolean bOnlySmart ); + int NET_DVR_FindNextLog_V30(int lLogHandle, NET_DVR_LOG_V30 lpLogData); + boolean NET_DVR_FindLogClose_V30(int lLogHandle); +//截止2004年8月5日,共113个接口 +//ATM DVR + int NET_DVR_FindFileByCard(int lUserID,int lChannel,int dwFileType, int nFindType, String sCardNumber, NET_DVR_TIME lpStartTime, NET_DVR_TIME lpStopTime); +//截止2004年10月5日,共116个接口 + +//JPEG抓图保存的本地 + boolean NET_DVR_CaptureJPEGPicture(int lUserID, int lChannel, NET_DVR_JPEGPARA lpJpegPara, String sPicFileName); +//JPEG抓图到内存 + boolean NET_DVR_CaptureJPEGPicture_NEW(int lUserID, int lChannel, NET_DVR_JPEGPARA lpJpegPara, Pointer sJpegPicBuffer, int dwPicSize, IntByReference lpSizeReturned); +//设置抓图模式 +boolean NET_DVR_SetCapturePictureMode(int dwCaptureMode); +// + + + +//2006-02-16 + int NET_DVR_GetRealPlayerIndex(int lRealHandle); + int NET_DVR_GetPlayBackPlayerIndex(int lPlayHandle); + +//2006-08-28 704-640 缩放配置 + boolean NET_DVR_SetScaleCFG(int lUserID, int dwScale); + boolean NET_DVR_GetScaleCFG(int lUserID, IntByReference lpOutScale); + boolean NET_DVR_SetScaleCFG_V30(int lUserID, NET_DVR_SCALECFG pScalecfg); + boolean NET_DVR_GetScaleCFG_V30(int lUserID, NET_DVR_SCALECFG pScalecfg); +//2006-08-28 ATM机端口设置 + boolean NET_DVR_SetATMPortCFG(int lUserID, short wATMPort); + boolean NET_DVR_GetATMPortCFG(int lUserID, ShortByReference LPOutATMPort); + +//2006-11-10 支持显卡辅助输出 + boolean NET_DVR_InitDDrawDevice(); + boolean NET_DVR_ReleaseDDrawDevice(); + int NET_DVR_GetDDrawDeviceTotalNums(); + boolean NET_DVR_SetDDrawDevice(int lPlayPort, int nDeviceNum); + + boolean NET_DVR_PTZSelZoomIn(int lRealHandle, NET_DVR_POINT_FRAME pStruPointFrame); + boolean NET_DVR_PTZSelZoomIn_EX(int lUserID, int lChannel, NET_DVR_POINT_FRAME pStruPointFrame); + +//解码设备DS-6001D/DS-6001F + boolean NET_DVR_StartDecode(int lUserID, int lChannel, NET_DVR_DECODERINFO lpDecoderinfo); + boolean NET_DVR_StopDecode(int lUserID, int lChannel); + boolean NET_DVR_GetDecoderState(int lUserID, int lChannel, NET_DVR_DECODERSTATE lpDecoderState); +//2005-08-01 + boolean NET_DVR_SetDecInfo(int lUserID, int lChannel, NET_DVR_DECCFG lpDecoderinfo); + boolean NET_DVR_GetDecInfo(int lUserID, int lChannel, NET_DVR_DECCFG lpDecoderinfo); + boolean NET_DVR_SetDecTransPort(int lUserID, NET_DVR_PORTCFG lpTransPort); + boolean NET_DVR_GetDecTransPort(int lUserID, NET_DVR_PORTCFG lpTransPort); + boolean NET_DVR_DecPlayBackCtrl(int lUserID, int lChannel, int dwControlCode, int dwInValue,IntByReference LPOutValue, NET_DVR_PLAYREMOTEFILE lpRemoteFileInfo); + boolean NET_DVR_StartDecSpecialCon(int lUserID, int lChannel, NET_DVR_DECCHANINFO lpDecChanInfo); + boolean NET_DVR_StopDecSpecialCon(int lUserID, int lChannel, NET_DVR_DECCHANINFO lpDecChanInfo); + boolean NET_DVR_DecCtrlDec(int lUserID, int lChannel, int dwControlCode); + boolean NET_DVR_DecCtrlScreen(int lUserID, int lChannel, int dwControl); + boolean NET_DVR_GetDecCurLinkStatus(int lUserID, int lChannel, NET_DVR_DECSTATUS lpDecStatus); + +//多路解码器 +//2007-11-30 V211支持以下接口 //11 + boolean NET_DVR_MatrixStartDynamic(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_DYNAMIC_DEC lpDynamicInfo); + boolean NET_DVR_MatrixStopDynamic(int lUserID, int dwDecChanNum); + boolean NET_DVR_MatrixGetDecChanInfo(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_DEC_CHAN_INFO lpInter); + boolean NET_DVR_MatrixSetLoopDecChanInfo(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_LOOP_DECINFO lpInter); + boolean NET_DVR_MatrixGetLoopDecChanInfo(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_LOOP_DECINFO lpInter); + boolean NET_DVR_MatrixSetLoopDecChanEnable(int lUserID, int dwDecChanNum, int dwEnable); + boolean NET_DVR_MatrixGetLoopDecChanEnable(int lUserID, int dwDecChanNum, IntByReference lpdwEnable); + boolean NET_DVR_MatrixGetLoopDecEnable(int lUserID, IntByReference lpdwEnable); + boolean NET_DVR_MatrixSetDecChanEnable(int lUserID, int dwDecChanNum, int dwEnable); + boolean NET_DVR_MatrixGetDecChanEnable(int lUserID, int dwDecChanNum, IntByReference lpdwEnable); + boolean NET_DVR_MatrixGetDecChanStatus(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_DEC_CHAN_STATUS lpInter); +//2007-12-22 增加支持接口 //18 + boolean NET_DVR_MatrixSetTranInfo(int lUserID, NET_DVR_MATRIX_TRAN_CHAN_CONFIG lpTranInfo); + boolean NET_DVR_MatrixGetTranInfo(int lUserID, NET_DVR_MATRIX_TRAN_CHAN_CONFIG lpTranInfo); + boolean NET_DVR_MatrixSetRemotePlay(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_DEC_REMOTE_PLAY lpInter); + boolean NET_DVR_MatrixSetRemotePlayControl(int lUserID, int dwDecChanNum, int dwControlCode, int dwInValue, IntByReference LPOutValue); + boolean NET_DVR_MatrixGetRemotePlayStatus(int lUserID, int dwDecChanNum, NET_DVR_MATRIX_DEC_REMOTE_PLAY_STATUS lpOuter); +//end + boolean NET_DVR_RefreshPlay(int lPlayHandle); +//恢复默认值 + boolean NET_DVR_RestoreConfig(int lUserID); +//保存参数 + boolean NET_DVR_SaveConfig(int lUserID); +//重启 + boolean NET_DVR_RebootDVR(int lUserID); +//关闭DVR + boolean NET_DVR_ShutDownDVR(int lUserID); +//参数配置 begin + boolean NET_DVR_GetDeviceConfig(int lUserID, int dwCommand,int dwCount, Pointer lpInBuffer, int dwInBufferSize, Pointer lpStatusList,Pointer lpOutBuffer, int dwOutBufferSize); + boolean NET_DVR_SetDeviceConfig(int lUserID, int dwCommand,int dwCount, Pointer lpInBuffer, int dwInBufferSize, Pointer lpStatusList,Pointer lpInParamBuffer, int dwInParamBufferSize); + boolean NET_DVR_GetDVRConfig(int lUserID, int dwCommand, int lChannel, Pointer lpOutBuffer, int dwOutBufferSize, IntByReference lpBytesReturned); + boolean NET_DVR_SetDVRConfig(int lUserID, int dwCommand,int lChannel, Pointer lpInBuffer, int dwInBufferSize); + boolean NET_DVR_GetSTDConfig(int lUserID, int dwCommand, NET_DVR_STD_CONFIG lpConfigParam); + boolean NET_DVR_SetSTDConfig(int lUserID, int dwCommand, NET_DVR_STD_CONFIG lpConfigParam); + boolean NET_DVR_GetDVRWorkState_V30(int lUserID, NET_DVR_WORKSTATE_V30 lpWorkState); + boolean NET_DVR_GetDVRWorkState(int lUserID, NET_DVR_WORKSTATE lpWorkState); + boolean NET_DVR_SetVideoEffect(int lUserID, int lChannel, int dwBrightValue, int dwContrastValue, int dwSaturationValue, int dwHueValue); + boolean NET_DVR_GetVideoEffect(int lUserID, int lChannel, IntByReference pBrightValue, IntByReference pContrastValue, IntByReference pSaturationValue, IntByReference pHueValue); + boolean NET_DVR_ClientGetframeformat(int lUserID, NET_DVR_FRAMEFORMAT lpFrameFormat); + boolean NET_DVR_ClientSetframeformat(int lUserID, NET_DVR_FRAMEFORMAT lpFrameFormat); + boolean NET_DVR_ClientGetframeformat_V30(int lUserID, NET_DVR_FRAMEFORMAT_V30 lpFrameFormat); + boolean NET_DVR_ClientSetframeformat_V30(int lUserID, NET_DVR_FRAMEFORMAT_V30 lpFrameFormat); + boolean NET_DVR_GetAlarmOut_V30(int lUserID, NET_DVR_ALARMOUTSTATUS_V30 lpAlarmOutState); + boolean NET_DVR_GetAlarmOut(int lUserID, NET_DVR_ALARMOUTSTATUS lpAlarmOutState); + boolean NET_DVR_SetAlarmOut(int lUserID, int lAlarmOutPort,int lAlarmOutStatic); + +//测温 +boolean NET_DVR_CaptureJPEGPicture_WithAppendData(int lUserID, int iChannelNum, NET_DVR_JPEGPICTURE_WITH_APPENDDATA m_strJpegWithAppendData); + +//视频参数调节 + boolean NET_DVR_ClientSetVideoEffect(int lRealHandle,int dwBrightValue,int dwContrastValue, int dwSaturationValue,int dwHueValue); + boolean NET_DVR_ClientGetVideoEffect(int lRealHandle,IntByReference pBrightValue,IntByReference pContrastValue, IntByReference pSaturationValue,IntByReference pHueValue); + +//配置文件 + boolean NET_DVR_GetConfigFile(int lUserID, String sFileName); + boolean NET_DVR_SetConfigFile(int lUserID, String sFileName); + boolean NET_DVR_GetConfigFile_V30(int lUserID, String sOutBuffer, int dwOutSize, IntByReference pReturnSize); + + boolean NET_DVR_GetConfigFile_EX(int lUserID, String sOutBuffer, int dwOutSize); + boolean NET_DVR_SetConfigFile_EX(int lUserID, String sInBuffer, int dwInSize); + +//启用日志文件写入接口 + boolean NET_DVR_SetLogToFile(int bLogEnable , String strLogDir, boolean bAutoDel ); + boolean NET_DVR_GetSDKState( NET_DVR_SDKSTATE pSDKState); + boolean NET_DVR_GetSDKAbility( NET_DVR_SDKABL pSDKAbl); + boolean NET_DVR_GetPTZProtocol(int lUserID, NET_DVR_PTZCFG pPtzcfg); +//前面板锁定 + boolean NET_DVR_LockPanel(int lUserID); + boolean NET_DVR_UnLockPanel(int lUserID); + + boolean NET_DVR_SetRtspConfig(int lUserID, int dwCommand, NET_DVR_RTSPCFG lpInBuffer, int dwInBufferSize); + boolean NET_DVR_GetRtspConfig(int lUserID, int dwCommand, NET_DVR_RTSPCFG lpOutBuffer, int dwOutBufferSize); + + boolean NET_DVR_ContinuousShoot(int lUserID, NET_DVR_SNAPCFG lpInter); + boolean NET_DVR_ManualSnap(int lUserID, NET_DVR_MANUALSNAP lpInter, NET_DVR_PLATE_RESULT lpOuter); + + public static interface FRemoteConfigCallback extends Callback { + public void invoke(int dwType, Pointer lpBuffer, int dwBufLen, Pointer pUserData); + } + +int NET_DVR_StartRemoteConfig (int lUserID, int dwCommand, Pointer lpInBuffer, int dwInBufferLen, FRemoteConfigCallback cbStateCallback, Pointer pUserData); +boolean NET_DVR_SendRemoteConfig(int lHandle, int dwDataType, Pointer pSendBuf, int dwBufSize); +int NET_DVR_GetNextRemoteConfig (int lHandle, Pointer lpOutBuff, int dwOutBuffSize); +int NET_DVR_SendWithRecvRemoteConfig(int lHandle, Pointer lpInBuff, int dwInBuffSize, Pointer lpOutBuff, int dwOutBuffSize, IntByReference dwOutDataLen); +boolean NET_DVR_StopRemoteConfig(int lHandle); +boolean NET_DVR_RemoteControl(int lUserID, int dwCommand, Pointer lpInBuffer, int dwInBufferSize); + +boolean NET_DVR_STDXMLConfig(int lUserID, NET_DVR_XML_CONFIG_INPUT lpInputParam, NET_DVR_XML_CONFIG_OUTPUT lpOutputParam); +boolean NET_DVR_GetSTDAbility(int lUserID, int dwAbilityType, NET_DVR_STD_ABILITY lpAbilityParam); +boolean NET_DVR_GetDeviceAbility(int lUserID, int dwAbilityType, Pointer pInBuf, int dwInLength, Pointer pOutBuf, int dwOutLength); +boolean NET_DVR_ControlGateway(int lUserID, int lGatewayIndex, int dwStaic); +boolean NET_DVR_InquestStartCDW_V30(int lUserID, NET_DVR_INQUEST_ROOM lpInquestRoom, boolean bNotBurn); +boolean NET_DVR_InquestStopCDW_V30(int lUserID, NET_DVR_INQUEST_ROOM lpInquestRoom, boolean bCancelWrite); +boolean NET_DVR_GetArrayList(int lUserID, NET_DVR_ARRAY_LIST lpArrayList); + +boolean NET_DVR_GetLocalIP(byte[] strIP, IntByReference pValidNum, boolean pEnableBind); +boolean NET_DVR_SetValidIP(int dwIPIndex, boolean bEnableBind); + +boolean NET_DVR_AlarmHostAssistantControl(int lUserID, int dwType, int dwNumber, int dwCmdParam); + + //gps相关结构定义 +public static class TimeSegParam extends Structure{ + //GPS数据查找起始时间 + public NET_DVR_TIME struBeginTime; + //GPS数据查找结束时间 + public NET_DVR_TIME struEndTime; + //GPS点时间间隔,单位:秒 + public int dwInterval; + //保留 + public byte[] byRes = new byte[76]; +} + +//按时间点查询 +public static class TimePointParam extends Structure{ + //GPS数据查找时间点 + public NET_DVR_TIME struTimePoint ; + //保留 + public byte[] byRes = new byte[104]; +} + +public static class GpsDataParamUion extends Union{ + //按时间段查询 + public TimeSegParam timeSeg = new TimeSegParam(); + //按时间点查询 + public TimePointParam timePoint = new TimePointParam(); +} + +//gps查询参数定义 +public static class NET_DVR_GET_GPS_DATA_PARAM extends Structure { + //查找方式:0- 按时间段查找GPS数据,1- 按时间点查找GPS数据 + public int dwCmdType; + public GpsDataParamUion gpsDataParam; + + public void read() + { + super.read(); + switch(dwCmdType) + { + case 0: + gpsDataParam.setType(TimeSegParam.class); + break; + case 1: + gpsDataParam.setType(TimePointParam.class); + break; + default: + break; + } + gpsDataParam.read(); + } + public void write() + { + super.write(); + gpsDataParam.write(); + } +} + +//gps数据结构定义 +public static class NET_DVR_GPS_INFO extends Structure { + public byte[] byDirection = new byte[2]; + public byte bySvs; + public byte byLocateMode; + public short wHDOP; + public short wHeight; + public int dwLatitude; + public int dwLongitude; + public int dwVehicleSpeed; + public int dwVehicleDirection; + public byte[] byRes= new byte[8]; +} + +//gps返回数据结构定义 +public static class NET_DVR_GPS_DATA extends Structure { + public NET_DVR_GPS_INFO struGPSInfo; + public NET_DVR_TIME struTime; + public byte[] byRes = new byte[12]; +} + + public static interface fGPSDataCallback extends Library { + public void invoke(int nHandle, int dwState, Pointer lpBuffer, int dwBufLen, Pointer pUser); + } + + int NET_DVR_GetVehicleGpsInfo(int lUserID, NET_DVR_GET_GPS_DATA_PARAM lpGPSDataParam, fGPSDataCallback cbGPSDataCallback, Pointer pUser); + +} + +//windows gdi接口,gdi32.dll in system32 folder, 在设置遮挡区域,移动侦测区域等情况下使用 +interface GDI32 extends W32API +{ + GDI32 INSTANCE = (GDI32) Native.loadLibrary("gdi32", GDI32.class, DEFAULT_OPTIONS); + + public static final int TRANSPARENT = 1; + + int SetBkMode(HDC hdc, int i); + + HANDLE CreateSolidBrush(int icolor); +} + +//windows user32接口,user32.dll in system32 folder, 在设置遮挡区域,移动侦测区域等情况下使用 +interface USER32 extends W32API +{ + + USER32 INSTANCE = (USER32) Native.loadLibrary("user32", USER32.class, DEFAULT_OPTIONS); + + public static final int BF_LEFT = 0x0001; + public static final int BF_TOP = 0x0002; + public static final int BF_RIGHT = 0x0004; + public static final int BF_BOTTOM = 0x0008; + public static final int BDR_SUNKENOUTER = 0x0002; + public static final int BF_RECT = (BF_LEFT | BF_TOP | BF_RIGHT | BF_BOTTOM); + + boolean DrawEdge(HDC hdc, RECT qrc, int edge, int grfFlags); + + int FillRect(HDC hDC, RECT lprc, HANDLE hbr); +} + + + + diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/Thermometry/JpegWithAppendData.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/Thermometry/JpegWithAppendData.java new file mode 100644 index 0000000..8b38a04 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/Thermometry/JpegWithAppendData.java @@ -0,0 +1,208 @@ +package com.yfd.platform.modules.deviceapi.Thermometry; + + +import com.yfd.platform.modules.deviceapi.HCNetSDK; +import com.yfd.platform.utils.FileUtil; +import lombok.extern.slf4j.Slf4j; + +import java.io.*; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Created by panting6 on 2021/2/21. + */ +@Slf4j +public class JpegWithAppendData { + + static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; + + public void jpegWithAppendData(int lUserID, int iChannelNum) { + boolean bRet = false; + HCNetSDK.NET_DVR_JPEGPICTURE_WITH_APPENDDATA m_strJpegWithAppendData = + new HCNetSDK.NET_DVR_JPEGPICTURE_WITH_APPENDDATA(); + m_strJpegWithAppendData.dwSize = m_strJpegWithAppendData.size(); + HCNetSDK.BYTE_ARRAY ptrJpegByte = new HCNetSDK.BYTE_ARRAY(4 * 1024 * 1024); + HCNetSDK.BYTE_ARRAY ptrP2PDataByte = new HCNetSDK.BYTE_ARRAY(2 * 1024 * 1024); + HCNetSDK.BYTE_ARRAY ptrVisiblePicByte = new HCNetSDK.BYTE_ARRAY(40 * 1024 * 1024); + m_strJpegWithAppendData.pJpegPicBuff = ptrJpegByte.getPointer(); + m_strJpegWithAppendData.pP2PDataBuff = ptrP2PDataByte.getPointer(); + m_strJpegWithAppendData.pVisiblePicBuff = ptrVisiblePicByte.getPointer(); + m_strJpegWithAppendData.dwJpegPicLen = 4 * 1024 * 1024; + m_strJpegWithAppendData.dwP2PDataLen = 2 * 1024 * 1024; + m_strJpegWithAppendData.dwVisiblePicLen = 40 * 1024 * 1024; + m_strJpegWithAppendData.write(); + + bRet = hCNetSDK.NET_DVR_CaptureJPEGPicture_WithAppendData(lUserID, iChannelNum, m_strJpegWithAppendData); + + if (bRet == true) { + m_strJpegWithAppendData.read(); + log.info("NET_DVR_CaptureJPEGPicture_WithAppendData调用成功!图片和温度数据已保存在pic文件夹中,热成像有效区域:{" + + m_strJpegWithAppendData.struThermalValidRect.fX + "," + m_strJpegWithAppendData.struThermalValidRect.fY + + "," + m_strJpegWithAppendData.struThermalValidRect.fWidth + "," + m_strJpegWithAppendData.struThermalValidRect.fHeight + "},可见光有效区域:{" + + m_strJpegWithAppendData.struVisibleValidRect.fX + "," + m_strJpegWithAppendData.struVisibleValidRect.fY + "," + m_strJpegWithAppendData.struVisibleValidRect.fWidth + + "," + m_strJpegWithAppendData.struVisibleValidRect.fHeight + "}"); + //测温数据 + if (m_strJpegWithAppendData.dwP2PDataLen > 0) { + SimpleDateFormat sf = new SimpleDateFormat("yyyymmddHHmmss"); + String newName = sf.format(new Date()); + File dir = new File(".\\pic\\"); + if (!dir.exists()) { + dir.mkdir(); + } + FileOutputStream fout; + BufferedWriter fout1; + try { + fout = new FileOutputStream(".\\pic\\" + "测温" + newName + ".data"); + ByteBuffer bufferss = m_strJpegWithAppendData.pP2PDataBuff.getByteBuffer(0, + m_strJpegWithAppendData.dwP2PDataLen); + byte[] bytess = new byte[m_strJpegWithAppendData.dwP2PDataLen]; + bufferss.rewind(); + bufferss.get(bytess); + fout.write(bytess); + fout.close(); + fout1 = new BufferedWriter(new FileWriter(dir + "\\测温" + newName + ".csv")); + String tempdata = ""; + byte[] byTempData = new byte[4]; + for (int i = 0; i < m_strJpegWithAppendData.dwJpegPicHeight; i++) { + for (int j = 0; j < m_strJpegWithAppendData.dwJpegPicWidth; j++) { + ByteBuffer TempDatabuffers = + m_strJpegWithAppendData.pP2PDataBuff.getByteBuffer((j + i * m_strJpegWithAppendData.dwJpegPicWidth) * 4, 4); + TempDatabuffers.get(byTempData); + int l; + l = byTempData[0]; + l &= 0xff; + l |= ((long) byTempData[1] << 8); + l &= 0xffff; + l |= ((long) byTempData[2] << 16); + l &= 0xffffff; + l |= ((long) byTempData[3] << 24); + tempdata = " " + Float.intBitsToFloat(l); + fout1.write(tempdata); + fout1.write(","); + } + fout1.newLine(); + } + fout1.close(); + } catch (FileNotFoundException e) { + log.error("路径不存在"); + } catch (IOException e) { + log.error("异常"); + } + } + //测温图片 + if (m_strJpegWithAppendData.dwJpegPicLen > 0) { + SimpleDateFormat sf = new SimpleDateFormat("yyyymmddHHmmss"); + String newName = sf.format(new Date()); + FileOutputStream pic; + FileOutputStream pic1; + FileOutputStream pic2; + try { + pic = new FileOutputStream(".\\pic\\" + "测温_T" + newName + ".jpg"); + pic1 = new FileOutputStream(".\\pic\\" + "测温_T" + newName + ".data"); + pic2 = new FileOutputStream(".\\pic\\" + "测温_V" + newName + ".jpg"); + ByteBuffer buffers = m_strJpegWithAppendData.pJpegPicBuff.getByteBuffer(0, + m_strJpegWithAppendData.dwJpegPicLen); + ByteBuffer buffers1 = m_strJpegWithAppendData.pVisiblePicBuff.getByteBuffer(0, + m_strJpegWithAppendData.dwVisiblePicLen); + byte[] bytes = new byte[m_strJpegWithAppendData.dwJpegPicLen]; + byte[] bytes1 = new byte[m_strJpegWithAppendData.dwVisiblePicLen]; + buffers.rewind(); + buffers.get(bytes); + buffers1.rewind(); + buffers1.get(bytes1); + pic.write(bytes); + pic1.write(bytes); + pic2.write(bytes1); + pic.close(); + pic1.close(); + pic2.close(); + + } catch (FileNotFoundException e) { + log.error("路径不存在"); + } catch (IOException e) { + log.error("异常"); + } + } + } else { + log.error("抓图叠加温度信息失败,错误码" + hCNetSDK.NET_DVR_GetLastError()); + } + } + + public String[][] jpegWithAppendData1(int lUserID, int iChannelNum, String csvFilePath) { + boolean bRet = false; + HCNetSDK.NET_DVR_JPEGPICTURE_WITH_APPENDDATA m_strJpegWithAppendData = + new HCNetSDK.NET_DVR_JPEGPICTURE_WITH_APPENDDATA(); + m_strJpegWithAppendData.dwSize = m_strJpegWithAppendData.size(); + HCNetSDK.BYTE_ARRAY ptrJpegByte = new HCNetSDK.BYTE_ARRAY(4 * 1024 * 1024); + HCNetSDK.BYTE_ARRAY ptrP2PDataByte = new HCNetSDK.BYTE_ARRAY(2 * 1024 * 1024); + HCNetSDK.BYTE_ARRAY ptrVisiblePicByte = new HCNetSDK.BYTE_ARRAY(40 * 1024 * 1024); + m_strJpegWithAppendData.pJpegPicBuff = ptrJpegByte.getPointer(); + m_strJpegWithAppendData.pP2PDataBuff = ptrP2PDataByte.getPointer(); + m_strJpegWithAppendData.pVisiblePicBuff = ptrVisiblePicByte.getPointer(); + m_strJpegWithAppendData.dwJpegPicLen = 4 * 1024 * 1024; + m_strJpegWithAppendData.dwP2PDataLen = 2 * 1024 * 1024; + m_strJpegWithAppendData.dwVisiblePicLen = 40 * 1024 * 1024; + m_strJpegWithAppendData.write(); + + bRet = hCNetSDK.NET_DVR_CaptureJPEGPicture_WithAppendData(lUserID, iChannelNum, m_strJpegWithAppendData); + + if (bRet == true) { + m_strJpegWithAppendData.read(); + log.error("NET_DVR_CaptureJPEGPicture_WithAppendData调用成功!图片和温度数据已保存在pic文件夹中,热成像有效区域:{" + + m_strJpegWithAppendData.struThermalValidRect.fX + "," + m_strJpegWithAppendData.struThermalValidRect.fY + + "," + m_strJpegWithAppendData.struThermalValidRect.fWidth + "," + m_strJpegWithAppendData.struThermalValidRect.fHeight + "},可见光有效区域:{" + + m_strJpegWithAppendData.struVisibleValidRect.fX + "," + m_strJpegWithAppendData.struVisibleValidRect.fY + "," + m_strJpegWithAppendData.struVisibleValidRect.fWidth + + "," + m_strJpegWithAppendData.struVisibleValidRect.fHeight + "}"); + //测温数据 + if (m_strJpegWithAppendData.dwP2PDataLen > 0) { + //String csvFilePath + BufferedWriter fout1; + try { + csvFilePath = csvFilePath.replaceAll("jpg", "csv"); + log.error(csvFilePath); + fout1 = new BufferedWriter(new FileWriter(csvFilePath)); + String tempdata = ""; + byte[] byTempData = new byte[4]; + String[][] temp = + new String[m_strJpegWithAppendData + .dwJpegPicWidth][m_strJpegWithAppendData.dwJpegPicHeight]; + for (int i = 0; i < m_strJpegWithAppendData.dwJpegPicHeight; i++) { + for (int j = 0; j < m_strJpegWithAppendData.dwJpegPicWidth; j++) { + ByteBuffer TempDatabuffers = + m_strJpegWithAppendData.pP2PDataBuff.getByteBuffer((j + i * m_strJpegWithAppendData.dwJpegPicWidth) * 4, 4); + TempDatabuffers.get(byTempData); + int l; + l = byTempData[0]; + l &= 0xff; + l |= ((long) byTempData[1] << 8); + l &= 0xffff; + l |= ((long) byTempData[2] << 16); + l &= 0xffffff; + l |= ((long) byTempData[3] << 24); + tempdata = " " + Float.intBitsToFloat(l); + temp[j][i] = tempdata.trim(); + fout1.write(tempdata); + fout1.write(","); + } + fout1.newLine(); + } + fout1.close(); + String hexFilePath = csvFilePath; + hexFilePath = hexFilePath.replaceAll("csv", "fir"); + FileUtil.convertCsvToBinary(csvFilePath, hexFilePath); + return temp; + } catch (FileNotFoundException e) { + log.error("路径不存在"); + } catch (IOException e) { + log.error("异常"); + } + } + } else { + log.error("抓图叠加温度信息失败,错误码" + hCNetSDK.NET_DVR_GetLastError()); + } + + return null; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/Thermometry/ThreadGetOnceTempData.java b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/Thermometry/ThreadGetOnceTempData.java new file mode 100644 index 0000000..75f52ef --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/deviceapi/Thermometry/ThreadGetOnceTempData.java @@ -0,0 +1,34 @@ +package com.yfd.platform.modules.deviceapi.Thermometry; + +import com.sun.jna.ptr.IntByReference; +import com.yfd.platform.modules.deviceapi.HCNetSDK; + +import java.text.DecimalFormat; + +/** + * Created by panting6 on 2021/1/25. + */ +public class ThreadGetOnceTempData { + + static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE; + + + public String threadGetOnceTempData(int lUserID, int RuleID) { + HCNetSDK.NET_DVR_THERMOMETRYRULE_TEMPERATURE_INFO m_TherTemInfo = + new HCNetSDK.NET_DVR_THERMOMETRYRULE_TEMPERATURE_INFO(); + IntByReference lpBytesReturned = new IntByReference(0); + if (!hCNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_THERMOMETRYRULE_TEMPERATURE_INFO, RuleID, + m_TherTemInfo.getPointer(), 16 * 1024, lpBytesReturned)) { + return ""; + } else { + m_TherTemInfo.read(); + // 取小数点后一位 + DecimalFormat format = new DecimalFormat("0.0"); + float fMaxTemperature = m_TherTemInfo.fMaxTemperature; + float fMinTemperature = m_TherTemInfo.fMinTemperature; + String max = format.format(Double.valueOf(fMaxTemperature)); + String min = format.format(Double.valueOf(fMinTemperature)); + return max + "," + min; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AlarmLogController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AlarmLogController.java new file mode 100644 index 0000000..834bcbd --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AlarmLogController.java @@ -0,0 +1,292 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.DeviceChannel; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.service.IDeviceChannelService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; +import com.yfd.platform.modules.patroltask.service.IAlarmLogService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 告警日志表 前端控制器 + *

+ * + * + * @since 2023-05-03 + */ +@RestController +@RequestMapping("/patroltask/task-alarm") +@Api(value = "AlarmLogController", tags = "告警日志") +public class AlarmLogController { + + @Resource + private IAlarmLogService alarmLogService; + + @Resource + private IDeviceChannelService deviceChannelService; + + @Resource + private ITaskTodoService taskTodoService; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @GetMapping("/getAlarmLogList") + @ApiOperation("查询报警信息") + public ResponseResult getAlarmLogList(String stationId) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("未传变电站信息"); + } + List idList = Arrays.asList(stationId.split(",")); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(AlarmLog::getStationId, idList).eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getCheckFlag, 0).select(AlarmLog::getId, + AlarmLog::getContent, AlarmLog::getAlarmLevel, AlarmLog::getAlarmDate, AlarmLog::getStationName).orderByDesc(AlarmLog::getAlarmDate); + List> maps = alarmLogService.listMaps(queryWrapper); + return ResponseResult.successData(maps); + } + + @GetMapping("/getNotCheckAlarmCount") + @ApiOperation("查询未审核告警和数量") + public ResponseResult getNotCheckAlarmCount(String stationId) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.successData(null); + } + String[] split = stationId.split(","); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getDatastatus, "1").in(AlarmLog::getStationId, Arrays.asList(split)).eq(AlarmLog::getCheckFlag + , 0).orderByDesc(AlarmLog::getAlarmDate); + List list = alarmLogService.list(queryWrapper); + Map map = new HashMap<>(); + map.put("count", list.size()); + map.put("notCheck", list); + return ResponseResult.successData(map); + } + + @GetMapping("/getNotCheckAlarmInfo") + @ApiOperation("根据任务id查询未审核告警和数量") + public ResponseResult getNotCheckAlarmInfo(String taskTodoIds) { + List> list = taskTodoService.getNotCheckAlarmInfo(taskTodoIds); + Map map = new HashMap<>(); + map.put("count", list.size()); + map.put("notCheck", list); + return ResponseResult.successData(map); + } + + @GetMapping("/getExceptionEventStat") + @ApiOperation("异常事件统计") + public ResponseResult getExceptionEventStat(String stationId) { + if (StrUtil.isBlank(stationId)) { + return ResponseResult.error("未传变电站信息"); + } + List idList = Arrays.asList(stationId.split(",")); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(AlarmLog::getStationId, idList).isNotNull(AlarmLog::getAlarmLevel).eq(AlarmLog::getDatastatus, "1").select(AlarmLog::getId, + AlarmLog::getContent, AlarmLog::getAlarmLevel, AlarmLog::getAlarmDate, AlarmLog::getStationName); + List> maps = alarmLogService.listMaps(queryWrapper); + Map>> alarmLevel = maps.stream().collect(Collectors.groupingBy(m -> m.get( + "alarmLevel"))); + List> maps1 = alarmLevel.get("1"); + List> maps2 = alarmLevel.get("2"); + List> maps3 = alarmLevel.get("3"); + List> maps4 = alarmLevel.get("4"); + Map alarmMap = new HashMap<>(); + if (maps1 == null || maps1.size() == 0) { + alarmMap.put("warnEvent", 0); + } else { + alarmMap.put("warnEvent", maps1.size()); + } + if (maps2 == null || maps2.size() == 0) { + alarmMap.put("sameEvent", 0); + } else { + alarmMap.put("sameEvent", maps2.size()); + } + if (maps3 == null || maps3.size() == 0) { + alarmMap.put("seriousEvent", 0); + } else { + alarmMap.put("seriousEvent", maps3.size()); + } + if (maps4 == null || maps4.size() == 0) { + alarmMap.put("crisisEvent", 0); + } else { + alarmMap.put("crisisEvent", maps4.size()); + } + return ResponseResult.successData(alarmMap); + } + + @GetMapping("/getAlarmListByType") + @ApiOperation("静默监视查询") + public ResponseResult getAlarmListByType(Page> page, String stationId, String areaId, + String patrolDeviceName, String monitorType) { + Page> alarmList = alarmLogService.getAlarmListByType(page, stationId, areaId, + patrolDeviceName, monitorType); + List> records = alarmList.getRecords(); + records.forEach(r -> { + String patroldeviceCode = r.get("patroldeviceCode").toString(); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(DeviceChannel::getDeviceid, patroldeviceCode).select(DeviceChannel::getDeviceid, + DeviceChannel::getChannelid); + List> channelList = deviceChannelService.listMaps(queryWrapper1); + if (channelList != null && channelList.size() > 0) { + r.put("channelList", channelList); + } + }); + alarmList.setRecords(records); + return ResponseResult.successData(alarmList); + } + + @GetMapping("/getAlarmListPage") + @ApiOperation("根据设备编号查询告警列表") + public ResponseResult getAlarmListPage(Page> page, String patrolDeviceCode, + String monitorType, String startDate, String endDate) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + Page> alarmList = alarmLogService.getAlarmListPage(page, patrolDeviceCode, monitorType, + startFormat, endFormat); + return ResponseResult.successData(alarmList); + } + + @GetMapping("/getAlarmLogPage") + @ApiOperation("告警信息确认") + public ResponseResult getAlarmLogPage(Page> page, String stationCode, String taskAlarmType, + String alarmLevel, String checkFlag, String startDate, String endDate) { + if (StrUtil.isBlank(stationCode)) { + return ResponseResult.error("未传变电站信息"); + } + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + Page> alarmList = alarmLogService.getAlarmLogPage(page, stationCode, taskAlarmType, + alarmLevel, checkFlag, startFormat, endFormat); + List> records = alarmList.getRecords(); + records.forEach(r -> { + if (ObjectUtil.isNotEmpty(r.get("deviceId"))) { + String deviceId = r.get("deviceId").toString(); + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + r.put("deviceClass", substationDevice.getDeviceClass()); + } + String patroldeviceCode = r.get("patroldeviceCode").toString(); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(DeviceChannel::getDeviceid, patroldeviceCode).select(DeviceChannel::getDeviceid, + DeviceChannel::getChannelid); + List> channelList = deviceChannelService.listMaps(queryWrapper1); + if (channelList != null && channelList.size() > 0) { + r.put("channelList", channelList); + } + }); + alarmList.setRecords(records); + return ResponseResult.successData(alarmList); + } + + @PostMapping("/setAlarmLogStatus") + @ApiOperation("告警信息核查") + public ResponseResult setAlarmLogStatus(@RequestBody AlarmLog alarmLog) { + if (alarmLog == null) { + return ResponseResult.error("参数为空"); + } + boolean ok = alarmLogService.setAlarmLogStatus(alarmLog); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("核查失败"); + } + + } + + @PostMapping("/setBatchAlarmLogStatus") + @ApiOperation("告警信息批量核查") + public ResponseResult batchSetAlarmLogStatus(String alarmJson) { + if (StrUtil.isBlank(alarmJson)) { + return ResponseResult.error("参数为空"); + } + JSONObject objects = new JSONObject(alarmJson); + Map alarm = objects.toBean(Map.class); + String ids = Objects.isNull(alarm.get("ids")) ? "" : alarm.get("ids").toString(); + if (StrUtil.isBlank(ids)) { + return ResponseResult.error("已经核查的信息不能再次核查"); + } + String[] split = ids.split(","); + String checkComment = Objects.isNull(alarm.get("checkComment")) ? "" : alarm.get("checkComment").toString(); + String checkFlag = Objects.isNull(alarm.get("checkFlag")) ? "" : alarm.get("checkFlag").toString(); + String currentUsername = SecurityUtils.getCurrentUsername(); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp); + for (String id : split) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.set(AlarmLog::getCheckComment, checkComment).set(AlarmLog::getCheckFlag, checkFlag).set(AlarmLog::getLastmodifier, currentUsername).set(AlarmLog::getCheckUserName, currentUsername).set(AlarmLog::getLastmodifydate, timestamp).set(AlarmLog::getCheckDate, format).eq(AlarmLog::getId, id); + alarmLogService.update(updateWrapper); + AlarmLog alarmLog = alarmLogService.getById(id); + Map logMap = BeanUtil.beanToMap(alarmLog); + String stationId = alarmLog.getStationId(); + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getStationId, stationId).eq(AlarmLog::getCheckFlag, 0); + int count = alarmLogService.count(queryWrapper1); + logMap.put("alarmCount", count); + JSONObject jsonObject = new JSONObject(logMap); + WebSocketServer.sendInfo("check_" + alarmLog.getStationId(), jsonObject.toString()); + } + return ResponseResult.success(); + } + + @GetMapping("/getAlarmHistory") + @ApiOperation("根据机器人编号查询告警历史(前20条)") + public ResponseResult getAlarmHistory(String patroldeviceCode) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getPatroldeviceCode, patroldeviceCode).select(AlarmLog::getAlarmDate,AlarmLog::getContent).orderByDesc(AlarmLog::getAlarmDate).last("LIMIT 20"); + List> mapList = alarmLogService.listMaps(queryWrapper); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getAlarmLogById") + @ApiOperation("根据告警信息ID查询详情") + public ResponseResult getAlarmLogById(String id) { + Map alarmLog = alarmLogService.getAlarmLogById(id); + + return ResponseResult.successData(alarmLog); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AnalyseCallController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AnalyseCallController.java new file mode 100644 index 0000000..0970791 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AnalyseCallController.java @@ -0,0 +1,290 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.system.OsInfo; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.config.AlgorithmServerConfig; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.IAlgorithmModelService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.IAlarmLogService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.VideoToImageUtil; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import net.coobird.thumbnailator.Thumbnails; +import org.apache.poi.util.IOUtils; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 智能巡视系统识别回调方法 + *

+ * + * @since 2023-05-03 + */ +@RestController +@RequestMapping("/") +@Slf4j +public class AnalyseCallController { + + @Resource + private IAlarmLogService alarmLogService; + + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private HttpRESTfulUtils httpUtil; + @Resource + private HttpServerConfig Config; + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + @Resource + private IAlgorithmModelService algorithmModelService; + @Resource + private AlgorithmServerConfig algorithmServerConfig; + @Resource + private TaskTodoMapper taskTodoMapper; + @Resource + private ISubstationDeviceService substationDeviceService; + + + @PostMapping("/picAnalyseRetNotify") + @ApiOperation("智能分析识别通知") + public Object picAnalyseRetNotify(@RequestBody String params) { + Map result = new HashMap<>(); + log.info("智能分析识别通知:" + params); + try { + boolean isJSON = JSONUtil.isTypeJSONObject(params); + if (isJSON) { + //正常处理 + JSONObject jsonObject = JSONUtil.parseObj(params); + String requestId = jsonObject.getStr("requestId");//对象ID + TaskTodo taskTodo = taskTodoMapper.selectById(requestId); + if (ObjUtil.isNotEmpty(taskTodo)) {//日常巡视,更新巡视结果 + alarmLogService.updateTaskResult(jsonObject); + } else { + SubstationDevice substationDevice = substationDeviceService.getById(requestId); + if(ObjUtil.isNotEmpty(substationDevice)){ + alarmLogService.updateDeviceResult(jsonObject); + }else{ + alarmLogService.createAlarm(jsonObject); + } + } + result.put("code", SystemCode.SUCCESS_STATUS_CODE.getCode());//正常 + } else { + result.put("code", SystemCode.REFUSE_STATUS_CODE.getCode()); + } + } catch (Exception e) { + log.error(e.getMessage());; + result.put("code", "500"); + } + return result; + } + + @PostMapping("/silentMonitorRetNotify") + @ApiOperation("静默监视时发现缺陷的识别通知") + public Object silentMonitorRetNotify(@RequestBody String params) { + Map result = new HashMap(); + try { + boolean isJSON = JSONUtil.isTypeJSONObject(params); + if (isJSON) { + //正常处理 + JSONObject jsonObject = JSONUtil.parseObj(params); + alarmLogService.createAlarm(jsonObject); + + result.put("code", SystemCode.SUCCESS_STATUS_CODE.getCode());//正常 + } else { + result.put("code", SystemCode.REFUSE_STATUS_CODE.getCode()); + } + } catch (Exception e) { + result.put("code", "500"); + } finally { + return result; + } + } + + @RequestMapping(value = "/snapfile", method = RequestMethod.GET) + @ApiOperation("智能分析调用图片") + public void getSnapImage(HttpServletResponse response, String filename) throws IOException { + String suffix = StrUtil.subAfter(filename, ".", true).toLowerCase(); + if (!"png".equals(suffix) && !"jpg".equals(suffix) && !"gif".equals(suffix) && !"mp4".equals(suffix)) { + throw new RuntimeException("非法文件类型"); + } + InputStream inputStream = null; + try { + OsInfo osInfo = new OsInfo(); + boolean isLinux = osInfo.isLinux(); + filename = URLDecoder.decode(filename, "utf-8"); + log.info("filename" + filename); + if (isLinux) { + filename = StrUtil.replace(filename, "\\", "/"); + } + String originfilename = Config.getSnapFilePath() + filename; + + inputStream = new FileInputStream(new File(originfilename)); + //set the content type and header + response.setContentType("image/jpeg"); + if("mp4".equals(suffix)){ + response.setContentType("video/mp4"); + } + response.setHeader("Content-Disposition", "attachment; filename=" + filename); + //copy the file to the output stream + IOUtils.copy(inputStream, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } finally { + inputStream.close(); + } + + } + + @GetMapping("/previewimage") + @ApiOperation("在线预览图片") + @ResponseBody + public void previewImage(HttpServletResponse response, String filename, String size, String type) throws IOException { + OsInfo osInfo = new OsInfo(); + boolean isLinux = osInfo.isLinux(); + filename = URLDecoder.decode(filename, "utf-8"); + if (isLinux) {//linux系统替换文件路径字符 + // 000001\2023\05\15\TEST0001\CCD\20230515_152108_测试间隔2_测试主设备1_测试巡视点位1_识别.jpg + filename = StrUtil.replace(filename, "\\", "/"); + } + String originfilename = ""; + + if (StrUtil.isNotEmpty(type) && type.equals("alarm")) {//报警显示不同目录 + originfilename = Config.getAlarmFilePath() + filename; + } else { + originfilename = Config.getSnapFilePath() + filename; + } + + if (StrUtil.isNotEmpty(size) && size.equals("small")) { + String temp = StrUtil.subBefore(originfilename, "_", true); + String tofilename = temp + "_small.jpg"; + if (!FileUtil.exist(tofilename)) { + Thumbnails.of(originfilename) + .size(80, 80) + .toFile(tofilename); + } + FileUtil.viewImage(tofilename, response); + } else { + FileUtil.viewImage(originfilename, response); + } + } + + @GetMapping("/playAudioFile") + @ApiOperation("在线在线播放音频文件") + @ResponseBody + public void playAudioFile(HttpServletResponse response, String filename) throws IOException { + OsInfo osInfo = new OsInfo(); + boolean isLinux = osInfo.isLinux(); + filename = URLDecoder.decode(filename, "utf-8"); + if (isLinux) {//linux系统替换文件路径字符 + filename = StrUtil.replace(filename, "\\", "/"); + } + String originfilename = Config.getSnapFilePath() + filename; + FileUtil.playAudio(originfilename, response); + } + + @GetMapping("/snapandviewimage") + @ApiOperation("截取视频流文件并预览图片") + @ResponseBody + public void snapandViewImage(HttpServletResponse response, String devicecode, String channelcode) throws Exception { + String fullfilename = Config.getSnapFilePath() + "snaptemp.jpg"; + String url = String.format("rtsp://%s:%s/rtp/%s_%s", Config.getMediaIp(), Config.getMonitorRtspPort(), + devicecode, channelcode); + List list = + substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getInternationalId, devicecode)); + if (list.size() <= 0) { + return; + } + SubstationPatroldevice substationPatroldevice = list.get(0); + if ("15".equals(substationPatroldevice.getType())) { + List> dockInfoBySn = + substationPatroldeviceService.getDockInfoBySn(substationPatroldevice.getInternationalId()); + if (dockInfoBySn.size() > 0) { + Map map = dockInfoBySn.get(0); + url = "rtmp://"+Config.getPatrolIp()+":1935" + map.get("dronePushUrl").toString(); + } + } + if ("dali".equals(substationPatroldevice.getManufacturer())) { + String channelinfo = substationPatroldevice.getChannelinfo(); + if (!JSONUtil.isTypeJSONArray(channelinfo)) { + return; + } + JSONArray jsonArray = JSONUtil.parseArray(channelinfo); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + if ("2".equals(jsonObject.getStr("channel_type"))) { + url = jsonObject.getStr("channel_rtspurl"); + } + } + } + VideoToImageUtil.getVideoPicture(Config.getffmpegPath(), url, fullfilename); + FileUtil.viewImage(fullfilename, response); + } + + private String getToken() { + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("Username", algorithmServerConfig.getUsername()); + jsonObject.putOnce("Password", algorithmServerConfig.getPassword()); + com.alibaba.fastjson.JSONObject responseData = httpUtil.sendHttpPost("json", + algorithmServerConfig.getServerIp(), algorithmServerConfig.getHttpPort(), "", "recogApi/token", + jsonObject, null); + if (!"200".equals(responseData.getString("code"))) { + return null; + } + return responseData.getString("Token"); + } + + @PostMapping("/algorithmUpdateResult") + @ApiOperation("分析主机返回模型上传结果") + public void algorithmUpdateResult(@RequestBody String params) { + JSONObject jsonObject = JSONUtil.parseObj(params); + String requestId = jsonObject.getStr("requestId"); + LambdaUpdateWrapper updateWrapper1 = new LambdaUpdateWrapper<>(); + updateWrapper1.set(AlgorithmModel::getRunstate, "1").set(AlgorithmModel::getRunstate, "0"); + algorithmModelService.update(updateWrapper1); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AlgorithmModel::getId, requestId).set(AlgorithmModel::getRunstate, "1"); + algorithmModelService.update(updateWrapper); +// AlgorithmModel algorithmModel = algorithmModelService.getById(requestId); +// JSONObject jsonObject1 = new JSONObject(); +// jsonObject1.putOnce("sectionId", algorithmModel.getSectionId()); +// jsonObject1.putOnce("stationId", algorithmModel.getStationId()); +// jsonObject1.putOnce("version", algorithmModel.getVersion()); +// String token = getToken(); +// httpUtil.sendHeaderHttpPost("json", algorithmServerConfig.getServerIp(), algorithmServerConfig.getHttpPort(), "", token, +// "recogApi/updateAlgorithmVersion", jsonObject1, null); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AnalyseIndexController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AnalyseIndexController.java new file mode 100644 index 0000000..df571d5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/AnalyseIndexController.java @@ -0,0 +1,95 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Date; +import java.util.Map; + +/** + *

+ * 指标 前端控制器 + *

+ * + * + * @since 2023-05-03 + */ +@RestController +@RequestMapping("/patroltask") +@Api(value = "AnalyseIndexController", tags = "可靠性指标") +public class AnalyseIndexController { + + @Resource + private ITaskTodoService taskTodoService; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private HttpServerConfig httpServerConfig; + + + @GetMapping("/getDeviceInspectionStat") + @ApiOperation("巡视设备巡检统计") + public ResponseResult getDeviceInspectionStat(Page> page, String patrolDeviceCode, + String patrolDeviceName, String type) { + + Page> mapPage = taskTodoService.getDeviceInspectionStat(page, patrolDeviceCode, + patrolDeviceName, type); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTaskDataStat") + @ApiOperation("巡视任务数据统计") + public ResponseResult getTaskDataStat(Page> page, String stationCode, String type, + String startDate, + String endDate) { + String startFormat = ""; + String endFormat = ""; + if ("3".equals(type)) { + startDate = startDate + "-01"; + Date parseStart = DateUtil.parse(startDate); + Date beginOfDay = DateUtil.beginOfMonth(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + endDate = endDate + "-01"; + Date parseEnd = DateUtil.parse(endDate); + //一月的结束 + Date endOfDay = DateUtil.endOfMonth(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } else { + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + } + Page> mapPage = taskTodoService.getTaskDataStat(page, stationCode, type, startFormat, endFormat); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/exportTaskDataStat") + @ApiOperation("导出巡视任务数据统计") + public void exportTaskDataStat(Page> page, String stationCode, String type, String startDate, String endDate, HttpServletResponse response) throws IOException { + taskTodoService.exportTaskDataStat(page,stationCode,type,startDate,endDate,response); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/DefectDeviceController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/DefectDeviceController.java new file mode 100644 index 0000000..0778c72 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/DefectDeviceController.java @@ -0,0 +1,74 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.patroltask.service.IDefectDeviceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Map; + +/** + *

+ * 前端控制器 + *

+ * + * @author zhengsl + * @since 2024-03-28 + */ +@RestController +@RequestMapping("/patroltask/defect-device") +@Api(value = "DefectDeviceController", tags = "缺陷记录") +public class DefectDeviceController { + + @Resource + private IDefectDeviceService defectDeviceService; + + @GetMapping("/getDefectDeviceList") + @ApiOperation("查询缺陷数据") + public ResponseResult getDefectDeviceList(Page> page, String stationCode, String areaId, + String bayId, String taskName, String deviceName, + String patroldeviceName, String componentName, String recognitionType, + String isDefectFlag, String startDate, String endDate) { + if (StrUtil.isBlank(stationCode)) { + return ResponseResult.error("未传变电站信息"); + } + return defectDeviceService.getDefectDeviceList(page, stationCode, areaId, bayId, taskName, deviceName, + patroldeviceName, componentName, recognitionType, isDefectFlag, startDate, endDate); + } + + @PostMapping("/setDefectDevice") + @ApiOperation("缺陷信息归入") + public ResponseResult setDefectDevice(String ids) { + boolean ok = defectDeviceService.setDefectDevice(ids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("归入失败"); + } + + } + + @GetMapping("/getDefectDeviceById") + @ApiOperation("根据id获取缺陷信息") + public ResponseResult getDefectDeviceById(String id) { + return defectDeviceService.getDefectDeviceById(id); + + } + + @PostMapping("/deleteDefectDevice") + @ApiOperation("缺陷信息删除") + public ResponseResult deleteDefectDevice(String ids) { + boolean ok = defectDeviceService.deleteDefectDevice(ids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/DockTaskController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/DockTaskController.java new file mode 100644 index 0000000..e84f3c3 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/DockTaskController.java @@ -0,0 +1,49 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.service.DockTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.io.UnsupportedEncodingException; + +/** + * @Date: 2024/7/5 14:02 + * @Description: + */ +@RestController +@RequestMapping("/dock/task") +@Api(value = "TaskTodoController", tags = "机巢服务接口") +@Slf4j +public class DockTaskController { + + @Resource + private DockTaskService dockTaskService; + + @PostMapping("/sendMediaFileUrl") + @ApiOperation("机巢服务发送文件路径") + public synchronized ResponseResult sendMediaFileUrl(@RequestBody String params) throws Exception { + JSONObject jsonObject = JSONUtil.parseObj(params); + String url = jsonObject.getStr("url"); + String jobId = jsonObject.getStr("jobId"); + String fileName = jsonObject.getStr("fileName"); + log.info(params); + // 保存无人机文件 + dockTaskService.saveMediaFile(jobId, fileName, url); + return ResponseResult.success(); + } + + + @GetMapping("/sendTaskResultData") + @ApiOperation("发送任务数据到分析主机") + public synchronized ResponseResult sendTaskResultData(String taskTodoId) throws Exception { + dockTaskService.sendTaskResultData(taskTodoId); + return ResponseResult.success(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/ExaminePlanController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/ExaminePlanController.java new file mode 100644 index 0000000..b5bfb1d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/ExaminePlanController.java @@ -0,0 +1,247 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; +import com.yfd.platform.modules.patroltask.service.IExaminePlanService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + *

+ * 检修计划表 前端控制器 + *

+ * + * @since 2023-04-08 + */ +@RestController +@RequestMapping("/patroltasks/examine-plan") +@Api(value = "ExaminePlanController", tags = "任务检修") +public class ExaminePlanController { + + @Resource + private IExaminePlanService examinePlanService; + @Resource + private ISubstationService substationService; + @Resource + private IStationRobotService stationRobotService; + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + @Resource + private HttpServerConfig httpServerConfig; + + @GetMapping("/getTaskList") + @ApiOperation("分页查询检修计划") + public ResponseResult getExaminePlanList(Page> page, String configName, String bindStatus) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(configName)) { + queryWrapper.like(ExaminePlan::getConfigName, configName); + } + if (StrUtil.isNotBlank(bindStatus)) { + if ("0".equals(bindStatus)) { + queryWrapper.isNull(ExaminePlan::getDeviceList).or().eq(ExaminePlan::getDeviceList, ""); + } else { + queryWrapper.isNotNull(ExaminePlan::getDeviceList).ne(ExaminePlan::getDeviceList, ""); + } + } + Page> mapPage = examinePlanService.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + records.forEach(r -> { + if (!Objects.isNull(r.get("stationId"))) { + String stationId = r.get("stationId").toString(); + Substation substation = substationService.getById(stationId); + r.put("isStationFlag", substation.getIsStationFlag()); + r.put("online", substation.getOnline()); + } + }); + mapPage.setRecords(records); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTaskById") + @ApiOperation("根据Id查询检修计划") + public ResponseResult getTaskById(String id) { + ExaminePlan examinePlan = examinePlanService.getById(id); + return ResponseResult.successData(examinePlan); + } + + @GetMapping("/getBindInfoById") + @ApiOperation("根据Id查询关联信息") + public ResponseResult getBindInfoById(String id, String deviceLevel) { + List> mapList = examinePlanService.getBindInfoById(id, deviceLevel); + return ResponseResult.successData(mapList); + } + + @Log(module = "任务检修", value = "新增检修计划") + @PostMapping("/addExaminePlan") + @ApiOperation("新增检修计划") + public ResponseResult addExaminePlan(@RequestBody ExaminePlan examinePlan) { + if (examinePlan == null) { + return ResponseResult.error("参数为空"); + } + // 检修编码 + String configCode = examinePlan.getConfigCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ExaminePlan::getConfigCode, configCode); + int count = examinePlanService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("检修计划已经存在"); + } + if (StrUtil.isBlank(examinePlan.getDeviceList())) { + examinePlan.setDeviceLevel(0); + examinePlan.setDatastatus("0"); + } else { + examinePlan.setDatastatus("1"); + } + examinePlan.setEnable(0); + examinePlan.setCreateTime(DateUtil.now()); + examinePlan.setLastmodifier(SecurityUtils.getCurrentUsername()); + examinePlan.setLastmodifydate(new Timestamp(DateUtil.current())); + boolean ok = examinePlanService.save(examinePlan); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "任务检修", value = "修改检修计划") + @PostMapping("/updateExaminePlan") + @ApiOperation("修改检修计划") + public ResponseResult updateExaminePlan(@RequestBody ExaminePlan examinePlan) { + if (examinePlan == null) { + return ResponseResult.error("参数为空"); + } + // 检修编码 + String configCode = examinePlan.getConfigCode(); + String id = examinePlan.getId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(ExaminePlan::getId, id).eq(ExaminePlan::getConfigCode, configCode); + int count = examinePlanService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("检修计划已经存在"); + } + String deviceList = examinePlan.getDeviceList(); + if (StrUtil.isBlank(deviceList)) { + examinePlan.setDeviceLevel(0); + examinePlan.setDatastatus("0"); + } else { + examinePlan.setDatastatus("1"); + } + examinePlan.setLastmodifier(SecurityUtils.getCurrentUsername()); + examinePlan.setLastmodifydate(new Timestamp(DateUtil.current())); + boolean ok = examinePlanService.updateById(examinePlan); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "任务检修", value = "设置检修计划是否启用") + @PostMapping("/setExaminePlanStatus") + @ApiOperation("设置检修计划是否启用") + public ResponseResult setExaminePlanStatus(String id, String enable) { + ExaminePlan examinePlan = examinePlanService.getById(id); + if (StrUtil.isBlank(examinePlan.getDeviceList())) { + return ResponseResult.error("未关联启用状态不可操作"); + } + examinePlan.setEnable(Integer.valueOf(enable)); + if (StrUtil.isNotBlank(examinePlan.getRobotCode())) { + // 检修发送数据 + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("config_code", examinePlan.getConfigCode()); + jsonObject.putOnce("enable", examinePlan.getEnable()); + jsonObject.putOnce("start_time", examinePlan.getStartTime()); + jsonObject.putOnce("end_time", examinePlan.getEndTime()); + jsonObject.putOnce("device_level", examinePlan.getDeviceLevel()); + jsonObject.putOnce("device_list", examinePlan.getDeviceList()); + jsonObject.putOnce("coordinate_pixel", examinePlan.getCoordinatePixel()); + String robotCode = examinePlan.getRobotCode(); + if (StrUtil.isNotBlank(robotCode)) { + List list = substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, robotCode)); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), + SystemCode.TYPE_EXAMINE_ISSUED_CODE.getCode(), SystemCode.COMMAND_EXAMINE_CONFIG_CODE.getCode(), examinePlan.getStationCode(), jsonObject.toString()); + } + } + } + boolean ok = examinePlanService.updateById(examinePlan); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "任务检修", value = "检修计划关联设备") + @PostMapping("/bindExaminePlan") + @ApiOperation("检修计划关联设备") + public ResponseResult bindExaminePlan(@RequestBody ExaminePlan examinePlan) { + if (examinePlan == null) { + return ResponseResult.error("参数为空"); + } + examinePlan.setLastmodifier(SecurityUtils.getCurrentUsername()); + examinePlan.setLastmodifydate(new Timestamp(DateUtil.current())); + String deviceList = examinePlan.getDeviceList(); + if (StrUtil.isBlank(deviceList)) { + examinePlan.setDeviceLevel(0); + } else { + examinePlan.setDatastatus("1"); + } + boolean ok = examinePlanService.updateById(examinePlan); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("关联失败"); + } + } + + @Log(module = "任务检修", value = "删除检修计划") + @PostMapping("/deleteExaminePlan") + @ApiOperation("删除检修计划") + public ResponseResult deleteExaminePlan(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] split = id.split(","); + for (String s : split) { + ExaminePlan examinePlan = examinePlanService.getById(s); + if (examinePlan.getEnable() == 1) { + continue; + } + examinePlanService.removeById(s); + } + return ResponseResult.success(); + } + + @GetMapping("/getBindExamineDevice") + @ApiOperation("根据Id查询检修计划绑定点位") + public ResponseResult getBindExamineDevice(String id) { + List deviceList = examinePlanService.getBindExamineDevice(id); + return ResponseResult.successData(deviceList); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskController.java new file mode 100644 index 0000000..55c66ba --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskController.java @@ -0,0 +1,390 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.patroltask.service.impl.QuartzMultiTaskManage; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.quartz.CronExpression; +import org.quartz.TriggerUtils; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.Year; +import java.time.YearMonth; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.*; + +/** + *

+ * 巡视任务表 前端控制器 + *

+ * + * @since 2023-04-08 + */ +@RestController +@RequestMapping("/patroltasks/task") +@Api(value = "TaskController", tags = "巡视任务") +public class TaskController { + + @Resource + private ITaskService taskService; + @Resource + private QuartzMultiTaskManage quartztaskManage; + @Resource + private IStationRobotService stationRobotService; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + + @GetMapping("/getTaskList") + @ApiOperation("分页查询巡视任务") + public ResponseResult getTaskList(Page> page, String type, String taskName, String enable, + String taskType, + String taskTodoType, + String startDate, + String endDate) throws ParseException { + if (StrUtil.isBlank(type)) { + return ResponseResult.error("参数为空"); + } + Page> taskList = taskService.getTaskList(page, startDate, endDate, type, taskName, enable + , taskType, taskTodoType); + return ResponseResult.successData(taskList); + } + + @Log(module = "巡视任务", value = "新增巡视任务", type = "1") + @PostMapping("/addTask") + @ApiOperation("新增巡视任务") + public ResponseResult addTask(@RequestBody Task task) { + String taskCode = task.getTaskCode(); + String type = task.getType(); + int count = + taskService.count(new LambdaQueryWrapper().eq(Task::getType, type).eq(Task::getTaskCode, taskCode).ne(Task::getDatastatus, "3")); + if (count > 0) { + return ResponseResult.error("该巡视任务已经存在"); + } + String cronValue = task.getCronValue(); + String taskTodoType = task.getTaskTodoType(); + if ("3".equals(taskTodoType) && !CronExpression.isValidExpression(cronValue)) { + return ResponseResult.error("cron表达式格式错误"); + } + // 设置优先级根据任务类型判断 + String taskType = task.getTaskType(); + LocalDateTime now = LocalDateTime.now(); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + String format = now.format(fmt); + task.setCreateTime(format); + String priority = task.getPriority(); + if (StrUtil.isBlank(priority)) { + task.setPriority(taskType); + } + task.setIsReport("0"); + boolean ok = taskService.addTask(task); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @GetMapping("/getTaskById") + @ApiOperation("根据Id查询巡视任务") + public ResponseResult getTaskById(String taskId) { + Task task = taskService.getById(taskId); + return ResponseResult.successData(task); + } + + @GetMapping("/getBindInfoById") + @ApiOperation("根据Id查询关联信息") + public ResponseResult getBindInfoById(String taskId, String deviceLevel) { + List> mapList = taskService.getBindInfoById(taskId, deviceLevel); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getTaskByStationId") + @ApiOperation("根据变电站Id查询巡视任务") + public ResponseResult getTaskByStationId(String stationId) { + List taskList = taskService.getTaskByStationId(stationId); + return ResponseResult.successData(taskList); + } + + @Log(module = "巡视任务", value = "巡视任务关联设备", type = "1") + @PostMapping("/bindTask") + @ApiOperation("巡视任务关联设备") + public ResponseResult bindTask(@RequestBody Task task) { + task.setLastmodifier(SecurityUtils.getCurrentUsername()); + String deviceList = task.getDeviceList(); + if (StrUtil.isNotBlank(deviceList)) { + task.setDeviceLevel(0); + } + task.setLastmodifydate(new Timestamp(DateUtil.current())); + boolean ok = taskService.updateById(task); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("关联失败"); + } + } + + @Log(module = "巡视任务", value = "修改巡视任务", type = "1") + @PostMapping("/updateTask") + @ApiOperation("修改巡视任务") + public ResponseResult updateTask(@RequestBody Task task) { + String taskCode = task.getTaskCode(); + String taskId = task.getTaskId(); + int count = + taskService.count(new LambdaQueryWrapper().ne(Task::getTaskId, taskId).eq(Task::getTaskCode, + taskCode).ne(Task::getDatastatus, "3")); + if (count > 0) { + return ResponseResult.error("该巡视任务已经存在"); + } + if (!"1".equals(task.getDatastatus())) { + return ResponseResult.error("初始创建状态可进行编辑"); + } + String cronValue = task.getCronValue(); + String taskTodoType = task.getTaskTodoType(); + if ("3".equals(taskTodoType) && !CronExpression.isValidExpression(cronValue)) { + return ResponseResult.error("cron表达式格式错误"); + } + // 设置优先级根据任务类型判断 + String taskType = task.getTaskType(); + String priority = task.getPriority(); + if (StrUtil.isBlank(priority)) { + task.setPriority(taskType); + } + String deviceList = task.getDeviceList(); + if (StrUtil.isBlank(deviceList)) { + task.setDeviceLevel(0); + } + task.setLastmodifier(SecurityUtils.getCurrentUsername()); + task.setLastmodifydate(new Timestamp(DateUtil.current())); + boolean ok = taskService.updateById(task); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "巡视任务", value = "设置巡视任务是否启用", type = "1") + @PostMapping("/setTaskStatus") + @ApiOperation("设置巡视任务是否启用") + public ResponseResult setTaskStatus(String taskId, String enable) throws ParseException { + Task task = taskService.getById(taskId); + String datastatus = task.getDatastatus(); + if (!"2".equals(datastatus)) { + return ResponseResult.error("复核后才可以改变状态"); + } + if (StrUtil.isBlank(task.getDeviceList())) { + return ResponseResult.error("未关联启用状态不可操作"); + } + task.setIsenable(enable); + boolean ok = taskService.updateById(task); + if ("1".equals(enable)) { + //启用 + String taskTodoType = task.getTaskTodoType(); + if (StrUtil.isNotBlank(task.getRobotCode())) { + //机器人任务启动 + List list = substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, task.getRobotCode())); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + if ("15".equals(substationPatroldevice.getType())) { + // 创建机巢任务 + if ("1".equals(taskTodoType)) { + TaskTodo taskTodo = taskService.createRunNowTask(task); + } else { + //定期或者周期执行 + taskService.createTodoTaskList(task); + } + } else { + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), SystemCode.TYPE_TASK_CONTROL_CODE.getCode(), SystemCode.COMMAND_TASK_START_CODE.getCode(), task.getTaskCode(), null); + } + + } + return ResponseResult.success(); + } + if ("1".equals(taskTodoType)) { + //立即执行 + TaskTodo taskTodo = taskService.createRunNowTask(task); + //立即执行当前任务 + quartztaskManage.addJob(taskTodo); + } else { + //定期或者周期执行 + taskService.createTodoTaskList(task); + } + } else if ("2".equals(enable)) { + taskService.deleteTodoTasks(task); + } + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "巡视任务", value = "巡视任务上报", type = "1") + @PostMapping("/setReportTask") + @ApiOperation("巡视任务上报") + public ResponseResult setReportTask(String taskId) { + if (StrUtil.isBlank(taskId)) { + return ResponseResult.error("参数为空"); + } + String[] split = taskId.split(","); + try { + for (String id : split) { + Task task = taskService.getById(id); + task.setIsReport("1"); + task.setLastmodifier(SecurityUtils.getCurrentUsername()); + task.setLastmodifydate(new Timestamp(DateUtil.current())); + taskService.updateById(task); + } + } catch (Exception e) { + throw new RuntimeException("上报失败"); + } + return ResponseResult.success(); + } + + @Log(module = "巡视任务", value = "复核巡视任务", type = "1") + @PostMapping("/checkTask") + @ApiOperation("复核巡视任务") + public ResponseResult checkTask(String taskId) throws ParseException { + Task task = taskService.getById(taskId); + if (!"1".equals(task.getDatastatus())) { + return ResponseResult.error("初始创建状态可进行审核"); + } + if (StrUtil.isNotBlank(task.getRobotCode())&&StrUtil.isBlank(task.getDockInfo())) { + + String fixedStartTime = task.getFixedStartTime(); + if (StrUtil.isBlank(fixedStartTime) && "1".equals(task.getTaskTodoType())) { + fixedStartTime = DateUtil.now(); + } + // 任务发送数据 + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("type", task.getType()); + jsonObject.putOnce("task_code", task.getTaskCode()); + jsonObject.putOnce("task_name", task.getTaskName()); + jsonObject.putOnce("priority", task.getPriority()); + jsonObject.putOnce("device_level", task.getDeviceLevel()); + jsonObject.putOnce("device_list", task.getDeviceList()); + jsonObject.putOnce("fixed_start_time", fixedStartTime); + jsonObject.putOnce("cycle_month", task.getCycleMonth()); + jsonObject.putOnce("cycle_week", task.getCycleWeek()); + jsonObject.putOnce("cycle_execute_time", task.getCycleExecuteTime()); + String cycle_start_time = ""; + String cycle_end_time = ""; + if ("3".equals(task.getTaskTodoType())) { + CronTriggerImpl cronTriggerImpl = new CronTriggerImpl(); + cronTriggerImpl.setCronExpression(task.getCronValue()); + Calendar calendar = Calendar.getInstance(); + Date begindate = calendar.getTime(); //当前时间 + // 获取当前年份 + Year currentYear = Year.now(); + + // 获取当前年份的最后一天(LocalDateTime会默认带上00:00:00的时间) + LocalDateTime lastDayOfYear = YearMonth.of(currentYear.getValue(), 12).atEndOfMonth().atStartOfDay(); + lastDayOfYear = lastDayOfYear.plusHours(23).plusMinutes(59).plusSeconds(59); + Date to = Date.from(lastDayOfYear.atZone(ZoneId.systemDefault()).toInstant()); + // 如果需要带上具体的时间(比如23:59:59),则进行相应的调整 + List dates = TriggerUtils.computeFireTimesBetween(cronTriggerImpl, null, begindate, to); + List dateList = new ArrayList<>(); + for (Date date : dates) { + if (StrUtil.isNotEmpty(task.getInvalidStartTime()) && StrUtil.isNotEmpty(task.getInvalidEndTime())) { + if (DateUtil.parse(task.getInvalidStartTime()).isBefore(date) && DateUtil.parse(task.getInvalidEndTime()).isAfter(date)) { + continue; + } + } + dateList.add(date); + } + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); + if (dateList.size() > 0) { + Date startdate = dateList.get(0); + Date enddate = dateList.get(dateList.size() - 1); + cycle_start_time = sdf.format(startdate); + cycle_end_time = sdf.format(enddate); + } + } + jsonObject.putOnce("cycle_start_time", cycle_start_time); + jsonObject.putOnce("cycle_end_time", cycle_end_time); + jsonObject.putOnce("interval_number", task.getIntervalNumber()); + jsonObject.putOnce("interval_type", task.getIntervalType()); + jsonObject.putOnce("interval_execute_time", task.getIntervalExecuteTime()); + jsonObject.putOnce("interval_start_time", task.getIntervalStartTime()); + jsonObject.putOnce("interval_end_time", task.getIntervalEndTime()); + jsonObject.putOnce("invalid_start_time", task.getInvalidStartTime()); + jsonObject.putOnce("invalid_end_time", task.getInvalidEndTime()); + jsonObject.putOnce("isenable", "0"); + jsonObject.putOnce("creator", task.getCreator()); + jsonObject.putOnce("create_time", task.getCreateTime()); + + List list = substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, task.getRobotCode())); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), + SystemCode.TYPE_TASK_ISSUED_CODE.getCode(), SystemCode.COMMAND_TASK_CONFIG_CODE.getCode(), task.getStationCode(), jsonObject.toString()); + } + if ("1".equals(task.getTaskTodoType())) { + // 如果是机器人立即执行任务复合,就把状态改成启用 + task.setIsenable("1"); + } + } + task.setDatastatus("2"); + task.setCreator(SecurityUtils.getCurrentUsername()); + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); + String format = now.format(fmt); + task.setCreateTime(format); + boolean ok = taskService.updateById(task); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("复核失败"); + } + } + + @Log(module = "巡视任务", value = "删除巡视任务", type = "1") + @PostMapping("/deleteTaskById") + @ApiOperation("删除巡视任务") + public ResponseResult deleteTaskById(String taskId) { + String[] split = taskId.split(","); + for (String id : split) { + Task task = taskService.getById(id); + if ("1".equals(task.getIsenable()) || "2".equals(task.getDatastatus())) { + return ResponseResult.error("删除失败,已含有复核生效的巡视任务"); + } + } + for (String id : split) { + Task task = taskService.getById(id); + task.setDatastatus("3"); + taskService.updateById(task); + } + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskDologController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskDologController.java new file mode 100644 index 0000000..efbd43c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskDologController.java @@ -0,0 +1,18 @@ +package com.yfd.platform.modules.patroltask.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 巡视任务执行日志 前端控制器 + *

+ * + * + * @since 2023-05-03 + */ +@RestController +@RequestMapping("/patroltask/task-dolog") +public class TaskDologController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskTodoController.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskTodoController.java new file mode 100644 index 0000000..7f2c5e4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/controller/TaskTodoController.java @@ -0,0 +1,1665 @@ +package com.yfd.platform.modules.patroltask.controller; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.deepoove.poi.XWPFTemplate; +import com.deepoove.poi.config.Configure; +import com.deepoove.poi.data.Pictures; +import com.deepoove.poi.plugin.table.HackLoopTableRenderPolicy; +import com.deepoove.poi.util.PoitlIOUtils; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.SubstationDeviceMapper; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.basedata.service.IWeatherLogService; +import com.yfd.platform.modules.patroltask.domain.DefectDevice; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.service.IDefectDeviceService; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.*; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.ClassUtils; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.renderable.RenderableImageProducer; +import java.io.*; +import java.net.URLDecoder; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 巡视任务执行表 前端控制器 + *

+ * + * @since 2023-04-08 + */ +@RestController +@RequestMapping("/patroltasks/task-todo") +@Api(value = "TaskTodoController", tags = "任务执行情况") +@Slf4j +public class TaskTodoController { + + @Resource + private ITaskTodoService taskTodoService; + + @Resource + private ITaskService taskService; + + @Resource + private SubstationDeviceMapper substationDeviceMapper; + + @Resource + private ISubstationDeviceService substationDeviceService; + + @Resource + private ISubstationService substationService; + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @Resource + private IWeatherLogService weatherLogService; + + @Resource + private IStationRobotService stationRobotService; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + IPlatformParentSystemService platformParentSystemService; + @Resource + private FtpClient ftpClient; + @Resource + private IDefectDeviceService defectDeviceService; + @Resource + private TLSFTPUtils tlsftpUtils; + + @GetMapping("/getTaskToDoList") + @ApiOperation("分页查询巡视任务执行情况") + public ResponseResult getTaskToDoList(Page> page, String taskId, String taskState) { + if (StrUtil.isBlank(taskId) || StrUtil.isBlank(taskState)) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(taskState)) { + List idList = new ArrayList<>(); + if ("0".equals(taskState)) { + idList.add("0"); + idList.add("2"); + idList.add("3"); + idList.add("5"); + } else { + idList.add("1"); + idList.add("4"); + idList.add("6"); + } + queryWrapper.in(TaskTodo::getTaskState, idList); + } + queryWrapper.eq(TaskTodo::getTaskId, taskId).orderByAsc(TaskTodo::getPlanStartTime); + Page> mapPage = taskTodoService.pageMaps(page, queryWrapper); + List> records = mapPage.getRecords(); + records.forEach(r -> { + + if (!Objects.isNull(r.get("stationId"))) { + String stationId = r.get("stationId").toString(); + Substation substation = substationService.getById(stationId); + r.put("isStationFlag", substation.getIsStationFlag()); + r.put("online", substation.getOnline()); + } + + }); + mapPage.setRecords(records); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTaskToDo") + @ApiOperation("查询巡视任务执行情况详情") + public ResponseResult getTaskToDo(String taskTodoId) { + Map map = taskTodoService.getTaskToDo(taskTodoId); + return ResponseResult.successData(map); + } + + /********************************** + * 用途说明: 根据任务ID,处理待处理任务 + * 参数说明 taskTodoId 待处理任务ID optType操作类型 + * pause:暂停;resume:暂停后恢复 runNow:立即执行 delete: 删除 stop:终止(不可恢复); + * optType=delete 删除或取消 + * 返回值说明: 当前任务信息 + ***********************************/ + @PostMapping("/doCurrentTask") + @ApiOperation("操作当前巡视任务") + public ResponseResult doCurrentTask(String taskTodoId, String optType) throws InterruptedException { + TaskTodo taskTodo = taskTodoService.getById(taskTodoId); + String robotCode = taskTodo.getRobotCode(); + String dockInfo = taskTodo.getDockInfo(); + if (StrUtil.isNotBlank(robotCode) && StrUtil.isBlank(dockInfo)) { + String Command = ""; + String Code = taskTodo.getTaskTodoId(); + String taskState = ""; + if (SystemCode.TASK_PAUSE_CODE.getCode().equals(optType)) { + Command = SystemCode.COMMAND_TASK_PAUSE_CODE.getCode(); + taskState = "3"; + } else if (SystemCode.TASK_RESUME_CODE.getCode().equals(optType)) { + Command = SystemCode.COMMAND_TASK_RESUME_CODE.getCode(); + taskState = "2"; + } else if (SystemCode.TASK_RUN_CODE.getCode().equals(optType)) { + Command = SystemCode.COMMAND_TASK_START_CODE.getCode(); + taskState = "2"; + Code = taskTodo.getTaskCode(); + } else if (SystemCode.TASK_STOP_CODE.getCode().equals(optType)) { + Command = SystemCode.COMMAND_TASK_STOP_CODE.getCode(); + taskState = "4"; + } + List list = substationPatroldeviceService.list(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, robotCode)); + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + stationRobotService.sendCommand(substationPatroldevice.getIpaddr(), httpServerConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), SystemCode.TYPE_TASK_CONTROL_CODE.getCode(), + Command, Code, null); + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(TaskTodo::getTaskTodoId, taskTodoId).set(TaskTodo::getTaskState, taskState); + taskTodoService.update(updateWrapper); + return ResponseResult.success(); + } + TaskTodo resut = taskTodoService.doCurrentTask(taskTodoId, optType); + return ResponseResult.successData(resut); + } + + /********************************** + * 用途说明: 根据任务识别结果更新数据,功能智能识别程序回调 + * 参数说明 taskResult 任务识别处理结果 + * 返回值说明: 当前任务信息 + ***********************************/ + @PostMapping("/callBackTaskResult") + @ApiOperation("更新当前任务智能识别结果") + public ResponseResult callBackTaskResult(TaskResult taskResult) { + boolean resut = taskTodoService.updateTaskResult(taskResult); + return ResponseResult.successData(resut); + } + + @GetMapping("/getTaskToDoByTaskId") + @ApiOperation("根据巡视任务查询任务执行情况") + public ResponseResult addTaskToDo(String taskId, String taskState) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskTodo::getTaskId, taskId).eq(TaskTodo::getTaskState, taskState); + List> listMaps = taskTodoService.listMaps(queryWrapper); + return ResponseResult.successData(listMaps); + } + + @GetMapping("/getTaskTodoStat") + @ApiOperation("巡视任务统计") + public ResponseResult getTaskTodoStat(String stationCode, String type) { + if (StrUtil.isBlank(stationCode)) { + return ResponseResult.error("未传变电站信息"); + } + Map mapTodo = new HashMap<>(); + mapTodo.put("toBeExecute", 0); + mapTodo.put("execute", 0); + mapTodo.put("unExecute", 0); + mapTodo.put("executeRate", 0); + mapTodo.put("toBeExecuteRate", 0); + mapTodo.put("allCount", 0); + LambdaQueryWrapper todoQueryWrapper = new LambdaQueryWrapper<>(); + todoQueryWrapper.eq(TaskTodo::getDatastatus, "1").in(TaskTodo::getStationCode, stationCode); + DateTime date = DateUtil.date(); + String formatDate; + if ("1".equals(type)) { + int year = DateUtil.year(date); + todoQueryWrapper.like(TaskTodo::getPlanStartTime, year); + } + if ("2".equals(type)) { + String format = DateUtil.format(date, "yyyy-MM"); + todoQueryWrapper.like(TaskTodo::getPlanStartTime, format); + } + if ("3".equals(type)) { + List weekDays = DateUtils.getWeekDays(date); + String start = weekDays.get(0); + String end = weekDays.get(weekDays.size() - 1); + Date parseEnd = DateUtil.parse(end); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + String endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + todoQueryWrapper.le(TaskTodo::getPlanStartTime, endFormat); + todoQueryWrapper.ge(TaskTodo::getPlanStartTime, start); + } + if ("4".equals(type)) { + formatDate = DateUtil.formatDate(date); + todoQueryWrapper.like(TaskTodo::getPlanStartTime, formatDate); + } + List listTodo = taskTodoService.list(todoQueryWrapper); + if (listTodo == null || listTodo.size() == 0) { + return ResponseResult.successData(mapTodo); + } + /*Map> collect = + listTodo.stream().collect(Collectors.groupingBy(TaskTodo::getStationCode)); + for (String code : collect.keySet()) { + + + }*/ + List executed = + listTodo.stream().filter(t -> ("1".equals(t.getTaskState()) || "2".equals(t.getTaskState()))).collect(Collectors.toList()); + List toBeExecuted = + listTodo.stream().filter(t -> ("0".equals(t.getTaskState()) || "3".equals(t.getTaskState()))).collect(Collectors.toList()); + List unExecuted = + listTodo.stream().filter(t -> ("4".equals(t.getTaskState()) || "5".equals(t.getTaskState()) || "6".equals(t.getTaskState()))).collect(Collectors.toList()); + int execute = executed.size(); + // 已执行 + mapTodo.put("execute", execute); + int toBeExecute = toBeExecuted.size(); + // 待执行 + mapTodo.put("toBeExecute", toBeExecute); + int allCount = listTodo.size(); + mapTodo.put("allCount", allCount); + mapTodo.put("executeRate", (int) (((float) execute / allCount) * 100 + 0.5)); + mapTodo.put("toBeExecuteRate", (int) (((float) toBeExecute / allCount) * 100 + 0.5)); + mapTodo.put("unExecute", unExecuted.size()); + return ResponseResult.successData(mapTodo); + } + + @GetMapping("/getAreaTaskTodoStat") + @ApiOperation("巡视任务统计(区域)") + public ResponseResult getAreaTaskTodoStat(String stationCode, String type) { + if (StrUtil.isBlank(stationCode)) { + return ResponseResult.error("未传变电站信息"); + } + LambdaQueryWrapper todoQueryWrapper = new LambdaQueryWrapper<>(); + List codeList = Arrays.asList(stationCode.split(",")); + todoQueryWrapper.eq(TaskTodo::getDatastatus, "1").in(TaskTodo::getStationCode, codeList); + //DateTime date = DateUtil.date(); + DateTime date = DateUtil.parse("2023-5-21"); + String formatDate; + if ("1".equals(type)) { + List weekDays = DateUtils.getWeekDays(date); + String start = weekDays.get(0); + String end = weekDays.get(weekDays.size() - 1); + Date parseEnd = DateUtil.parse(end); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + String endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + todoQueryWrapper.le(TaskTodo::getPlanStartTime, endFormat); + todoQueryWrapper.ge(TaskTodo::getPlanStartTime, start); + } + if ("2".equals(type)) { + formatDate = DateUtil.formatDate(date); + todoQueryWrapper.like(TaskTodo::getPlanStartTime, formatDate); + } + List listTodo = new ArrayList<>(taskTodoService.list(todoQueryWrapper)); + Map> collect = + listTodo.stream().collect(Collectors.groupingBy(TaskTodo::getStationCode)); + Map map = new HashMap<>(); + List stationList = new ArrayList<>(); + List> mapList = new ArrayList<>(); + for (String code : codeList) { + Substation substation = + substationService.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, code)); + stationList.add(substation.getStationName()); + List taskTodoList = collect.get(code); + Map mapTodo = new HashMap<>(); + if (taskTodoList != null && taskTodoList.size() > 0) { + long execute = + taskTodoList.stream().filter(t -> ("1".equals(t.getTaskState()) || "2".equals(t.getTaskState()))).count(); + long toBeExecute = + taskTodoList.stream().filter(t -> ("0".equals(t.getTaskState()) || "3".equals(t.getTaskState()))).count(); + long unExecute = + taskTodoList.stream().filter(t -> ("4".equals(t.getTaskState()) || "5".equals(t.getTaskState()) || "6".equals(t.getTaskState()))).count(); + // 已执行 + mapTodo.put("execute", execute); + // 待执行 + mapTodo.put("toBeExecute", toBeExecute); + int allCount = listTodo.size(); + mapTodo.put("allCount", allCount); + mapTodo.put("executeRate", (int) (((float) execute / allCount) * 100 + 0.5)); + mapTodo.put("toBeExecuteRate", (int) (((float) toBeExecute / allCount) * 100 + 0.5)); + mapTodo.put("unExecute", unExecute); + } else { + // 已执行 + mapTodo.put("execute", 0); + // 待执行 + mapTodo.put("toBeExecute", 0); + mapTodo.put("allCount", 0); + mapTodo.put("executeRate", 0); + mapTodo.put("toBeExecuteRate", 0); + mapTodo.put("unExecute", 0); + } + mapList.add(mapTodo); + } + map.put("xData", stationList); + map.put("yData", mapList); + return ResponseResult.successData(map); + } + + @GetMapping("/getTaskToDoListByStation") + @ApiOperation("巡视任务列表(前后10条)") + public ResponseResult getTaskToDoListByStation(String stationCode, String taskName) { + long position = taskTodoService.getLastTimePosition(stationCode, null, null, null, null); + // 获取正在执行中任务的前后十条 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskTodo::getStationCode, stationCode); +// if (StrUtil.isNotBlank(taskName)) { +// queryWrapper.like(TaskTodo::getTaskName, taskName); +// } + queryWrapper.ne(TaskTodo::getTaskType, "4"); + queryWrapper.orderByAsc(TaskTodo::getPlanStartTime).orderByAsc(TaskTodo::getTaskTodoId); + List> mapList = taskTodoService.listMaps(queryWrapper); + long start = 0; + long end = 0; + long size = mapList.size(); + if (position == 0 && size > 0) { + start = (size - 11) < 0 ? 0 : (size - 11); + end = size; + } + if (position > 0) { + start = (position - 11) < 0 ? 0 : (position - 11); + end = Math.min((position + 10), size); + } + List> mapList1 = mapList.subList((int) start, (int) end); + mapList1.forEach(m -> { + if (ObjectUtil.isNotEmpty(m.get("taskId"))) { + String taskId = m.get("taskId").toString(); + Task task = taskService.getById(taskId); + m.put("deviceLevel", task.getDeviceLevel()); + } + if (!Objects.isNull(m.get("planStartTime"))) { + DateTime dateTime = DateUtil.parse(m.get("planStartTime").toString()); + String dateFormat = DateUtil.format(dateTime, "dd日 HH:mm"); + m.put("formatDate", dateFormat); + } + + }); + if (StrUtil.isNotBlank(taskName)) { + List> mapList2 = + mapList1.stream().filter(m -> m.get("taskName").toString().contains(taskName)).collect(Collectors.toList()); + return ResponseResult.successData(mapList2); + } + return ResponseResult.successData(mapList1); + } + + @GetMapping("/getTaskToDoPage") + @ApiOperation("分页查询巡视任务执行情况(日历)") + public ResponseResult getTaskToDoPage(Page> page, String stationCode, String currentDate, + String startDate, String endDate, String status, String taskState, String taskName) { + /*List list = taskService.list(new LambdaQueryWrapper().eq(Task::getStationCode, stationCode)); + Set collect = list.stream().map(Task::getTaskId).collect(Collectors.toSet());*/ + // 获取当前时间最近的数据行 + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + + Map map2 = new HashMap<>(); + long position = taskTodoService.getLastTimePosition(stationCode, taskName, startFormat, endFormat, taskState); + long pageSize = 0; + if (position != 0) { + long size = page.getSize(); + pageSize = (position % size) == 0 ? (position / size) : (position / size) + 1; + } + map2.put("page", pageSize); + if ("1".equals(status)) { + if (pageSize == 0) { + pageSize = pageSize + 1; + } + page.setCurrent(pageSize); + } + Page> mapPage = taskTodoService.getTaskImplementation(page, stationCode, currentDate, + startFormat, endFormat, taskState, taskName); + map2.put("mapPage", mapPage); + return ResponseResult.successData(map2); + } + + @GetMapping("/getTaskToDoMonthPage") + @ApiOperation("分页查询巡视任务执行情况(月历)") + public ResponseResult getTaskToDoMonthPage(String stationCode, String year, String month) { + if (month.length() == 1) { + month = "0" + month; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(TaskTodo::getTaskType, "4").eq(TaskTodo::getStationCode, stationCode).like(TaskTodo::getPlanStartTime, year + "-" + month); + List> maps = taskTodoService.listMaps(queryWrapper); + maps.forEach(m -> { + String planStartTime = m.get("planStartTime").toString(); + DateTime parse = DateUtil.parse(planStartTime); + String dateStr = parse.toDateStr(); + m.put("groupDate", dateStr); + }); + Map>> groupDate = maps.stream().collect(Collectors.groupingBy(m -> m.get( + "groupDate"))); + List> listMap = new ArrayList<>(); + for (Object o : groupDate.keySet()) { + Map map = new HashMap<>(); + map.put("date", o.toString()); + List> maps1 = groupDate.get(o); + //将处理后的list3集合按日期升序排序 + //按照时间升序排列 + maps1.sort(Comparator.comparing(o2 -> o2.get("planStartTime").toString())); + map.put("list", maps1); + if (maps1.size() >= 3) { + List> maps2 = maps1.subList(0, 3); + map.put("list1", maps2); + } else { + map.put("list1", maps1); + } + map.put("status", "0"); + map.put("count", maps1.size()); + listMap.add(map); + } + return ResponseResult.successData(listMap); + } + + + @GetMapping("/getTaskToDoStatPage") + @ApiOperation("分页查询巡视任务执行情况统计(日历)") + public ResponseResult getTaskToDoStatList(String stationCode, String currentDate, String startDate, + String endDate, String taskName) { + + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + List> taskToDoStatList = taskTodoService.getTaskToDoStatList(stationCode, currentDate, startFormat, endFormat, null); + Map map = new HashMap<>(); + map.put("allCount", taskToDoStatList.size()); + + List> taskState = + taskToDoStatList.stream().filter(m -> "2".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("executing", taskState.size()); + List> taskState1 = + taskToDoStatList.stream().filter(m -> "1".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("execute", taskState1.size()); + List> taskState2 = + taskToDoStatList.stream().filter(m -> "3".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("suspend", taskState2.size()); + List> taskState3 = + taskToDoStatList.stream().filter(m -> "4".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("stop", taskState3.size()); + List> taskState0 = + taskToDoStatList.stream().filter(m -> "0".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("executed", taskState0.size()); + List> taskState5 = + taskToDoStatList.stream().filter(m -> "5".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("unexecuted", taskState5.size()); + List> taskState6 = + taskToDoStatList.stream().filter(m -> "6".equals(m.get("taskState").toString())).collect(Collectors.toList()); + map.put("overdue", taskState6.size()); + return ResponseResult.successData(map); + } + + @GetMapping("/getTaskReportList") + @ApiOperation("分页查询巡视报告") + public ResponseResult getTaskReportList(Page> page, String stationCode, String areaId, String taskName, + String type, String startDate, String endDate, String bayId, String patrolType, String mainDeviceId) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + Page> mapPage = taskTodoService.getTaskReportList(page, stationCode, areaId, taskName, + type, startFormat, endFormat, bayId, patrolType); + // List> records = mapPage.getRecords(); + // records.forEach(r -> { + // String taskTodoId = r.get("taskTodoId").toString(); + // Map map = taskTodoService.getTaskTodoById(taskTodoId,areaId,bayId,mainDeviceId); + // r.put("result", map); + // }); + return ResponseResult.successData(mapPage); + } + + @PostMapping("/deleteTaskTodo") + @ApiOperation("删除任务执行情况") + public ResponseResult deleteTaskTodo(String taskTodoId) { + if (StrUtil.isBlank(taskTodoId)) { + return ResponseResult.error("参数为空"); + } + String[] split = taskTodoId.split(","); + for (String id : split) { + taskTodoService.removeById(id); + } + return ResponseResult.success(); + } + + @GetMapping("/getTaskTodoById") + @ApiOperation("根据巡视任务执行情况ID查看详情") + public ResponseResult getTaskTodoById(String taskTodoId) { + Map map = taskTodoService.getTaskTodoById(taskTodoId, null, null, null); + return ResponseResult.successData(map); + } + + @GetMapping("/exportTaskTodoById") + @ApiOperation("导出报告") + public void exportTaskTodoById(String taskTodoId, String areaId, String bayId, String mainDeviceId, HttpServletResponse response) throws IOException { + Map map = taskTodoService.getTaskTodoById(taskTodoId, areaId, bayId, mainDeviceId); + if (map == null) { + return; + } + String stationCode = map.get("stationCode").toString(); + List list = + substationService.list(new LambdaQueryWrapper().eq(Substation::getDatastatus, "1").eq(Substation::getStationCode, stationCode)); + if (list == null || list.size() == 0) { + return; + } + Substation substation = list.get(0); + String stationId = substation.getStationId(); + // 获取环境信息 + Map weatherDevice = weatherLogService.getWeatherLogList(stationId); + map.put("environment", weatherDevice.get("environment")); + /* Map map = BeanUtil.beanToMap(taskTodo);*/ + map.put("stationName", substation.getStationName()); + map.put("stationType", substation.getStationType()); + map.put("voltLevel", substation.getVoltLevel()); + List> taskList = taskTodoService.getTaskResultById(map.get("taskTodoId").toString(), + areaId, bayId, mainDeviceId); + // 临时加的逻辑导出报告使用 + taskList.forEach(r -> { + if (ObjectUtil.isEmpty(r.get("valid1"))) { + r.put("valid1", "2"); + } + }); + List> abnormalList = + taskList.stream().filter(t -> !Objects.isNull(t.get("valid1")) && ("2").equals(t.get("valid1").toString())).collect(Collectors.toList()); + List> deviceByType = sysDictionaryItemsService.getDeviceByType("data_type"); + List> normalList = + taskList.stream().filter(t -> !Objects.isNull(t.get("valid1")) && ("1").equals(t.get("valid1").toString())).collect(Collectors.toList()); + long allCount = abnormalList.size() + normalList.size(); + if (allCount > 1000) { + throw new RuntimeException("文件内容过大无法预览或下载"); + } + abnormalList.forEach(a -> { + if (!Objects.isNull(a.get("dataType"))) { + String meterType = a.get("dataType").toString(); + for (Map data : deviceByType) { + String itemcode = data.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + a.put("dataType", data.get("dictname")); + break; + } + } + } + a.put("index", abnormalList.indexOf(a) + 1); + a.put("status", "异常"); + // 图片流 + try { + String filePath = ObjectUtil.isNotEmpty(a.get("defectFilePath")) ? a.get("defectFilePath").toString() : ""; + if (StrUtil.isBlank(filePath)) { + filePath = a.get("filePath").toString(); + } + if (StrUtil.isNotBlank(filePath)) { + filePath = URLDecoder.decode(filePath, "utf-8"); + filePath = filePath.replace("\\", "/"); + /*a.put("img", Pictures.ofStream(FileUtil.getImageStream(filePath), FileUtil.getPictureType + (filePath)) + .size(100, 120).create());*/ + log.info("报告图片路径" + httpServerConfig.getSnapFilePath() + filePath); + a.put("img", Pictures.ofStream(new FileInputStream(new File(httpServerConfig.getSnapFilePath() + filePath)), + FileUtil.getPictureType(filePath)) + .size(100, 120).create()); + } + + } catch (Exception e) { + log.error(e.getMessage());; + } + }); + normalList.forEach(n -> { + if (!Objects.isNull(n.get("dataType"))) { + String meterType = n.get("dataType").toString(); + for (Map data : deviceByType) { + String itemcode = data.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + n.put("dataType", data.get("dictname")); + break; + } + } + } + n.put("index", normalList.indexOf(n) + 1); + n.put("status", "正常"); + try { + String filePath = ObjectUtil.isNotEmpty(n.get("defectFilePath")) ? n.get("defectFilePath").toString() : ""; + if (StrUtil.isBlank(filePath)) { + filePath = n.get("filePath").toString(); + } + if (StrUtil.isNotBlank(filePath)) { + filePath = URLDecoder.decode(filePath, "utf-8"); + filePath = filePath.replace("\\", "/"); + log.info("报告图片路径" + httpServerConfig.getSnapFilePath() + filePath); + n.put("img", Pictures.ofStream(new FileInputStream(new File(httpServerConfig.getSnapFilePath() + filePath)), + FileUtil.getPictureType(filePath)) + .size(100, 120).create()); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + }); + List> demo = getMapData(); + //创建一个列表的规则 + HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy(); + //设置列表配置,如果有多个列表时需加.bind("list1", policy) 新列表配置即可 + Configure config = Configure.builder().bind("normalList", policy).bind("abnormalList", policy).build(); + if (abnormalList.size() > 0) { + map.put("abnormalList", abnormalList); + } else { + map.put("abnormalList", demo); + } + if (normalList.size() > 0) { + map.put("normalList", normalList); + } else { + map.put("normalList", demo); + } + + // 加载模板渲染数据 + String path = Objects.requireNonNull(Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource( + "")).getPath() + "templates" + File.separator + "巡视报告模板.docx"; + XWPFTemplate compile = XWPFTemplate.compile(path, config); + XWPFTemplate template = compile.render(map); + //template.write(new FileOutputStream("E:\\巡视报告模板.docx")); + response.setContentType("application/octet-stream"); + response.setHeader("Content-disposition", "attachment;filename='reportTemp.docx'"); + + OutputStream out = response.getOutputStream(); + BufferedOutputStream bos = new BufferedOutputStream(out); + template.write(bos); + bos.flush(); + out.flush(); + PoitlIOUtils.closeQuietlyMulti(template, bos, out); + } + + @GetMapping("/exportMeterDataByTaskTodoId") + @ApiOperation("导出表计数据") + public void exportMeterDataByTaskTodoId(String taskTodoId, HttpServletResponse response) throws IOException { + taskTodoService.exportMeterDataByTaskTodoId(taskTodoId,response); + } + + @GetMapping("/sendTaskResultById") + @ApiOperation("发送任务结果") + public void sendTaskResultById(String id) { + taskTodoService.sendTaskResultById(id); + } + + @GetMapping("/exportTaskToSystem") + @ApiOperation("导出报告到服务器") + public void exportTaskToSystem(String taskTodoId, String areaId, String bayId, String mainDeviceId) throws Exception { + String filename = "task_result_report.docx"; + String filepath = httpServerConfig.getSnapFilePath() + filename; + String filePath = this.exportTaskTodo(taskTodoId, areaId, bayId, mainDeviceId, filepath); + JSONObject jsonObject = new JSONObject(); + try { +// Thread.sleep(20000); + List list = platformParentSystemService.list(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + if (list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + if (tlsftpUtils.isConnected()) { + tlsftpUtils.disconnect(); + } + tlsftpUtils.connect(platformParentSystem.getFtpIp(), platformParentSystem.getFtpUser(), platformParentSystem.getFtpPassword(), NumberUtil.parseInt(platformParentSystem.getFtpPort())); + String path = String.format("/%s/%s/%s/%s/%s/", + "7FE4BB37-F54A-4808-852B-6CAB43DD71B3-00001", + DateUtil.format(DateUtil.date(), "yyyy"), + DateUtil.format(DateUtil.date(), "MM"), + DateUtil.format(DateUtil.date(), "dd"), + "report" + ); + jsonObject.putOnce("file_path", path + filePath); + tlsftpUtils.upload(path, new File(httpServerConfig.getSnapFilePath() + filePath)); + tlsftpUtils.disconnect(); + } + } catch (Exception e) { + tlsftpUtils.disconnect(); + } + platformParentSystemService.sendTaskReportCommand(jsonObject.toString()); + } + + private String exportTaskTodo(String taskTodoId, String areaId, String bayId, String mainDeviceId, String filepath) throws Exception { + Map map = taskTodoService.getTaskTodoById(taskTodoId, areaId, bayId, mainDeviceId); + if (map == null) { + return ""; + } + String stationCode = map.get("stationCode").toString(); + List list = + substationService.list(new LambdaQueryWrapper().eq(Substation::getDatastatus, "1").eq(Substation::getStationCode, stationCode)); + if (list == null || list.size() == 0) { + return ""; + } + Substation substation = list.get(0); + String stationId = substation.getStationId(); + // 获取环境信息 + Map weatherDevice = weatherLogService.getWeatherLogList(stationId); + map.put("environment", weatherDevice.get("environment")); + /* Map map = BeanUtil.beanToMap(taskTodo);*/ + map.put("stationName", substation.getStationName()); + map.put("stationType", substation.getStationType()); + map.put("voltLevel", substation.getVoltLevel()); + List> taskList = taskTodoService.getTaskResultById(map.get("taskTodoId").toString(), + areaId, bayId, mainDeviceId); + // 临时加的逻辑导出报告使用 + taskList.forEach(r -> { + if (ObjectUtil.isEmpty(r.get("valid1"))) { + r.put("valid1", "2"); + } + }); + List> abnormalList = + taskList.stream().filter(t -> !Objects.isNull(t.get("valid1")) && ("2").equals(t.get("valid1").toString())).collect(Collectors.toList()); + List> deviceByType = sysDictionaryItemsService.getDeviceByType("data_type"); + abnormalList.forEach(a -> { + if (!Objects.isNull(a.get("dataType"))) { + String meterType = a.get("dataType").toString(); + for (Map data : deviceByType) { + String itemcode = data.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + a.put("dataType", data.get("dictname")); + break; + } + } + } + a.put("index", abnormalList.indexOf(a) + 1); + a.put("status", "异常"); + // 图片流 + try { + String filePath = a.get("defectFilePath").toString(); + if (StrUtil.isNotBlank(filePath)) { + filePath = URLDecoder.decode(filePath, "utf-8"); + filePath = filePath.replace("\\", "/"); + a.put("img", + Pictures.ofStream(new FileInputStream(new File(httpServerConfig.getSnapFilePath() + filePath)), + FileUtil.getPictureType(filePath)) + .size(100, 120).create()); + } + + } catch (Exception e) { + log.error(e.getMessage());; + } + }); + List> normalList = + taskList.stream().filter(t -> !Objects.isNull(t.get("valid1")) && ("1").equals(t.get("valid1").toString())).collect(Collectors.toList()); + normalList.forEach(n -> { + if (!Objects.isNull(n.get("dataType"))) { + String meterType = n.get("dataType").toString(); + for (Map data : deviceByType) { + String itemcode = data.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + n.put("dataType", data.get("dictname")); + break; + } + } + } + n.put("index", normalList.indexOf(n) + 1); + n.put("status", "正常"); + try { + String filePath = n.get("defectFilePath").toString(); + if (StrUtil.isNotBlank(filePath)) { + filePath = URLDecoder.decode(filePath, "utf-8"); + filePath = filePath.replace("\\", "/"); + log.info("报告图片路径" + httpServerConfig.getSnapFilePath() + filePath); + n.put("img", + Pictures.ofStream(new FileInputStream(new File(httpServerConfig.getSnapFilePath() + filePath)), + FileUtil.getPictureType(filePath)) + .size(100, 120).create()); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + }); + List> demo = getMapData(); + + //创建一个列表的规则 + HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy(); + //设置列表配置,如果有多个列表时需加.bind("list1", policy) 新列表配置即可 + Configure config = Configure.builder().bind("normalList", policy).bind("abnormalList", policy).build(); + if (abnormalList.size() > 0) { + map.put("abnormalList", abnormalList); + } else { + map.put("abnormalList", demo); + } + if (normalList.size() > 0) { + map.put("normalList", normalList); + } else { + map.put("normalList", demo); + } + String planStartTime = map.get("planStartTime").toString(); + DateTime parse = DateUtil.parse(planStartTime); + String formatDate = DateUtil.format(parse, "yyyyMMddHHmmss"); + String filePath = map.get("taskName").toString() + formatDate + ".docx"; + filepath = filepath.replace("task_result_report.docx", filePath); + // 加载模板渲染数据 + String path = Objects.requireNonNull(Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource( + "")).getPath() + "templates" + File.separator + "巡视报告模板.docx"; + XWPFTemplate compile = XWPFTemplate.compile(path, config); + XWPFTemplate template = compile.render(map); + FileOutputStream fileOutputStream = new FileOutputStream(filepath); + template.write(fileOutputStream); + fileOutputStream.close(); + template.close(); + compile.close(); + return filePath; + } + + private List> getMapData() { + List> demo = new ArrayList<>(); + Map map = new HashMap<>(); + map.put("index", ""); + map.put("areaName", ""); + map.put("bayName", ""); + map.put("patroldeviceName", ""); + map.put("componentName", ""); + map.put("deviceName", ""); + map.put("dataType", ""); + map.put("time", ""); + map.put("desc", ""); + map.put("status", ""); + map.put("img", null); + demo.add(map); + return demo; + } + + @GetMapping("/getAbnormalDeviceList") + @ApiOperation("异常点位查询") + public ResponseResult getAbnormalDeviceList(Page> page, String stationCode, String areaId, + String bayId, String taskName, String deviceName, + String patroldeviceName, String componentName, String recognitionType + , String status, String startDate, String endDate) { + Page> mapPage = taskTodoService.getAbnormalDeviceList(page, stationCode, areaId, bayId, + taskName, deviceName, patroldeviceName, componentName, recognitionType, status, startDate, endDate); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTaskResultList") + @ApiOperation("数据对比分析列表") + public ResponseResult getTaskResultList(Page> page, String deviceId, String startDate, + String endDate) { + if (StrUtil.isBlank(deviceId)) { + return ResponseResult.error("参数为空"); + } + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + Page> resultList = taskTodoService.getTaskResultList(page, deviceId, startFormat, + endFormat); + List> records = resultList.getRecords(); + records.forEach(r -> { + String flag = r.get("flag").toString(); + String valid = ""; + if (Objects.isNull(r.get("reviseValid"))) { + if (!Objects.isNull(r.get("valid"))) { + valid = r.get("valid").toString(); + } + } else { + String reviseValid = r.get("reviseValid").toString(); + if ("0".equals(reviseValid)) { + valid = "1"; + } else { + valid = "2"; + } + } + if ("4".equals(flag)) { + r.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + r.put("analysisResult", "采集失败"); + } else if ("2".equals(valid)) { + r.put("analysisResult", "异常"); + } else if ("1".equals(valid)) { + r.put("analysisResult", "正常"); + } + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + if (substationDevice != null) { + String deviceClass = substationDevice.getDeviceClass(); + r.put("deviceClass", deviceClass); + } + }); + resultList.setRecords(records); + return ResponseResult.successData(resultList); + } + + @GetMapping("/exportTaskResultList") + @ApiOperation("导出数据对比分析列表") + public void exportTaskResultList(String deviceId, String startDate, String endDate, String exportFields, + HttpServletResponse response) throws IOException { + if (StrUtil.isBlank(deviceId)) { + return; + } + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + List> records = taskTodoService.getTaskResultAll(deviceId, startFormat, endFormat); + records.forEach(r -> { + String flag = ""; + if (!Objects.isNull(r.get("flag"))) { + flag = r.get("flag").toString(); + } + String valid = ""; + if (Objects.isNull(r.get("reviseValid"))) { + if (!Objects.isNull(r.get("valid"))) { + valid = r.get("valid").toString(); + } + } else { + String reviseValid = r.get("reviseValid").toString(); + if ("0".equals(reviseValid)) { + valid = "1"; + } else { + valid = "2"; + } + } + if ("4".equals(flag)) { + r.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + r.put("analysisResult", "采集失败"); + } else if ("2".equals(valid)) { + r.put("analysisResult", "异常"); + } else if ("1".equals(valid)) { + r.put("analysisResult", "正常"); + } + String deviceClass = ""; + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + if (substationDevice != null) { + deviceClass = substationDevice.getDeviceClass(); + } + r.put("deviceClass", deviceClass); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getPatroldeviceCode, r.get("patroldeviceCode").toString()); + queryWrapper.select(SubstationPatroldevice::getType); + Map map = substationPatroldeviceService.getMap(queryWrapper); + if (map != null) { + r.put("type", map.get("type")); + + } + }); + taskTodoService.exportTaskResultList(records, exportFields, response); + } + + @GetMapping("/previewTaskResultList") + @ApiOperation("预览数据对比分析列表") + public ResponseResult previewTaskResultList(String deviceId, String startDate, String endDate) { + if (StrUtil.isBlank(deviceId)) { + return ResponseResult.error("参数为空"); + } + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + List> records = taskTodoService.getTaskResultAll(deviceId, startFormat, endFormat); + records.forEach(r -> { + String flag = ""; + if (!Objects.isNull(r.get("flag"))) { + flag = r.get("flag").toString(); + } + String valid = ""; + if (Objects.isNull(r.get("reviseValid"))) { + if (!Objects.isNull(r.get("valid"))) { + valid = r.get("valid").toString(); + } + } else { + String reviseValid = r.get("reviseValid").toString(); + if ("0".equals(reviseValid)) { + valid = "1"; + } else { + valid = "2"; + } + } + if ("4".equals(flag)) { + r.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + r.put("analysisResult", "采集失败"); + } else if ("2".equals(valid)) { + r.put("analysisResult", "异常"); + } else if ("1".equals(valid)) { + r.put("analysisResult", "正常"); + } + String deviceClass = ""; + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + if (substationDevice != null) { + deviceClass = substationDevice.getDeviceClass(); + } + r.put("deviceClass", deviceClass); + }); + return ResponseResult.successData(records); + } + + @GetMapping("/getAnalysisCurve") + @ApiOperation("数据对比分析曲线") + public ResponseResult getAnalysisCurve(String deviceId, String startDate, String endDate) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + if (StrUtil.isBlank(deviceId)) { + return ResponseResult.error("参数为空"); + } + Map map = taskTodoService.getAnalysisCurve(deviceId, startFormat, endFormat); + return ResponseResult.successData(map); + } + + @GetMapping("/getTaskDeviceList") + @ApiOperation("根据任务执行情况ID查询点位查询") + public ResponseResult getTaskDeviceList(Page> page, String taskTodoId, String deviceName, + String valid) { + Page> mapPage = taskTodoService.getTaskDeviceList(page, taskTodoId, deviceName, valid); + List> records = mapPage.getRecords(); + records.forEach(m -> { + String flag = m.get("flag").toString(); + String valid1 = Objects.isNull(m.get("valid1")) ? "" : m.get("valid1").toString(); + if ("4".equals(flag)) { + m.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + m.put("analysisResult", "采集失败"); + } else if (StrUtil.isNotBlank(valid1) && "1".equals(valid1)) { + m.put("analysisResult", "正常"); + } else if (StrUtil.isNotBlank(valid1) && "2".equals(valid1)) { + m.put("analysisResult", "异常"); + } else { + m.put("analysisResult", ""); + } + }); + mapPage.setRecords(records); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTaskDeviceById") + @ApiOperation("根据任务执行情况ID查询点位查询(所有)") + public ResponseResult getTaskDeviceById(String taskTodoId, String deviceName, String valid) { + List> mapList = taskTodoService.getTaskDeviceById(taskTodoId, deviceName, valid); + mapList.forEach(m -> { + if (ObjUtil.isNotEmpty(m.get("patroldeviceJson"))) { +// JSONObject jobj = JSONUtil.parseObj(m.get("patroldeviceJson").toString()); + m.put("channelCode", m.get("patroldeviceChannelcode")); + m.put("channelPos", m.get("patroldevicePos")); + } + String flag = m.get("flag").toString(); + String valid1 = Objects.isNull(m.get("valid1")) ? "" : m.get("valid1").toString(); + if ("4".equals(flag)) { + m.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + m.put("analysisResult", "采集失败"); + } else if (StrUtil.isNotBlank(valid1) && "1".equals(valid1)) { + m.put("analysisResult", "正常"); + } else if (StrUtil.isNotBlank(valid1) && "2".equals(valid1)) { + m.put("analysisResult", "异常"); + } else { + m.put("analysisResult", ""); + } + }); + return ResponseResult.successData(mapList); + } + + @GetMapping("/exportAbnormalDevice") + @ApiOperation("导出异常点位(导出全部)") + public void exportAbnormalDevice(String stationCode, String taskName, + String deviceName, String patroldeviceName, + String componentName, String recognitionType, String status, + String startDate, String endDate, HttpServletResponse response) throws IOException { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + // 获取任务执行id + List> records = taskTodoService.getAbnormalDevice(stationCode, taskName, deviceName, + patroldeviceName, componentName, recognitionType, status, startFormat, endFormat); + taskTodoService.exportAbnormalDevice(records, response); + } + + @GetMapping("/getHistoricalCurve") + @ApiOperation("查询点位历史曲线") + public ResponseResult getHistoricalCurve(String deviceId) { + List> listMap = taskTodoService.getHistoricalCurve(deviceId); + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + String lowerValue = ""; + String upperValue = ""; + if (substationDevice != null) { + lowerValue = substationDevice.getLowerValue(); + upperValue = substationDevice.getUpperValue(); + } + listMap.forEach(m -> { + String time = m.get("time").toString(); + DateTime dateTime = DateUtil.parse(time); + String dateFormat = DateUtil.format(dateTime, "MM月dd日 HH时mm分"); + m.put("dateTime", dateFormat); + }); + List dateTimeList = listMap.stream().map(m -> m.get("dateTime")).collect(Collectors.toList()); + Collections.reverse(dateTimeList); + List valueList = listMap.stream().map(m -> m.get("value")).collect(Collectors.toList()); + Collections.reverse(valueList); + Map map = new HashMap<>(); + //map.put("list", listMap); + map.put("valueList", valueList); + map.put("dateTimeList", dateTimeList); + map.put("lowerValue", lowerValue); + map.put("upperValue", upperValue); + return ResponseResult.successData(map); + } + + @GetMapping("/getDeviceByMainDevice") + @ApiOperation("查询主设备所关联的点位") + public ResponseResult getDeviceByMainDevice(String mainDeviceId, String meterType, String recognitionType, + String startDate, String endDate) { + if (StrUtil.isBlank(mainDeviceId)) { + return ResponseResult.error("参数为空"); + } + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + List> maps = taskTodoService.getDeviceByMainDevice(mainDeviceId, meterType, recognitionType, + startFormat, endFormat); + return ResponseResult.successData(maps); + } + + @GetMapping("/test") + @ApiOperation("测试视频截图") + public void test() throws Exception { + //34020000001320000002_34020000001320000002 + // httpUtil.getMonitorVideoSnap("34020000001320000002","34020000001320000002"); + } + + /********************************** + * 用途说明: 修正巡视结果 + * 参数说明 taskResult 任务识别处理结果 + * 返回值说明: 当前任务信息 + ***********************************/ + @PostMapping("/updateTaskResultById") + @ApiOperation("修正巡视结果") + public ResponseResult updateTaskResultById(@RequestBody TaskResult taskResult) { + taskResult.setLastmodifier(SecurityUtils.getCurrentUsername()); + taskResult.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok = taskTodoService.updateTaskResultById(taskResult); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修正错误"); + } + + } + + /********************************** + * 用途说明: 修正巡视结果 + * 参数说明 taskResult 任务识别处理结果 + * 返回值说明: 当前任务信息 + ***********************************/ + @PostMapping("/updateTaskTodoById") + @ApiOperation("审核任务执行情况") + public ResponseResult updateTaskTodoById(@RequestBody TaskTodo taskTodo) throws Exception { + String currentUsername = SecurityUtils.getCurrentUsername(); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp); + taskTodo.setExamineDate(format); + taskTodo.setCexamineUserName(currentUsername); + taskTodo.setLastmodifier(currentUsername); + taskTodo.setLastmodifydate(timestamp); + boolean ok = taskTodoService.updateById(taskTodo); + // List abnormalResult = taskTodoService.getAbnormalResult(taskTodo.getTaskTodoId()); + // List defectDevices = BeanUtil.copyToList(abnormalResult, DefectDevice.class); + // defectDeviceService.saveOrUpdateBatch(defectDevices); + if (ok) { + // String filename = "task_result_report.docx"; + // String filepath = httpServerConfig.getSnapFilePath() + filename; + // String taskTodoId = taskTodo.getTaskTodoId(); + // this.exportTaskTodo(taskTodoId, null, null, null, filepath); + // JSONObject jsonObject = new JSONObject(); + // jsonObject.putOnce("task_patrolled_id", taskTodoId); + // jsonObject.putOnce("report_path", filename); + // platformParentSystemService.sendTaskReportCommand(jsonObject.toString()); + return ResponseResult.success(); + } else { + return ResponseResult.error("审核错误"); + } + + } + + /********************************** + * 用途说明: 查询联动控制巡视任务 + * 参数说明 stationCode 变电站编号 + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + @GetMapping("/getLinkTaskTodo") + @ApiOperation("查询联动控制巡视任务") + public ResponseResult getLinkTaskTodo(String stationCode, String todoId, String linkageType) { + List> mapList = taskTodoService.getLinkTaskTodo(stationCode, todoId, linkageType); + if (mapList == null || mapList.size() == 0) { + return ResponseResult.successData(null); + } + Map map = mapList.get(0); + if (ObjectUtil.isNotEmpty(map.get("taskTodoId"))) { + String taskTodoId = map.get("taskTodoId").toString(); + List> taskResultList = taskTodoService.getTaskResultById(taskTodoId, null, null, null); +// device_id_list + String deviceIdList = map.get("deviceIdList").toString(); + List ids = StrUtil.split(deviceIdList, ","); + List> sortList = new ArrayList<>(); + for (int i = ids.size() - 1; i >= 0; i--) { + String id = ids.get(i); + for (Map t : taskResultList) { + String deviceId = t.get("deviceId").toString(); + if (id.equals(deviceId)) { + if (ObjectUtil.isNotEmpty(t.get("patroldeviceCode")) && ObjectUtil.isNotEmpty(t.get( + "patroldeviceChannelcode"))) { + t.put("deviceId", t.get("patroldeviceCode").toString()); + t.put("channelId", t.get("patroldeviceChannelcode").toString()); + + } + String flag = ObjectUtil.isEmpty(t.get("flag")) ? "" : t.get("flag").toString(); + String valid1 = ObjectUtil.isEmpty(t.get("valid1")) ? "" : t.get("valid1").toString(); + if ("4".equals(flag)) { + t.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + t.put("analysisResult", "采集失败"); + } else if (StrUtil.isNotBlank(valid1) && "1".equals(valid1)) { + t.put("analysisResult", "正常"); + } else if (StrUtil.isNotBlank(valid1) && "2".equals(valid1)) { + t.put("analysisResult", "异常"); + } else { + t.put("analysisResult", ""); + } + sortList.add(t); + break; + } + } + } + map.put("children", sortList); + } + return ResponseResult.successData(map); + } + + @GetMapping("/getLinkTaskRecordLast") + @ApiOperation("查询联动任务最近十条") + public ResponseResult getLinkTaskRecordLast(String stationCode, String linkageType, String taskName) { + List> mapList = taskTodoService.getLinkTaskRecordLast(stationCode, linkageType, taskName); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getLinkTaskTodoList") + @ApiOperation("查询联动任务列表") + public ResponseResult getLinkTaskTodoList(Page> page, String stationCode, + String linkageDeviceName, String taskName, String linkageType, + String linkageDeviceType, String startDate, String endDate) { + if (StrUtil.isBlank(stationCode)) { + return ResponseResult.error("参数为空"); + } + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + + Page> mapPage = taskTodoService.getLinkTaskTodoList(page, stationCode, linkageDeviceName, + taskName, linkageType, linkageDeviceType, startFormat, endFormat); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTaskDurationStat") + @ApiOperation("按照日、周、月统计巡视任务") + public ResponseResult getTaskDurationStat(String startDate, String endDate, String type) { + List> mapList = taskTodoService.getTaskDurationStat(startDate, endDate, type); + return ResponseResult.successData(mapList); + } + + @GetMapping("/exportTaskDurationStat") + @ApiOperation("按照日、周、月统计巡视任务导出") + public void exportTaskDurationStat(String startDate, String endDate, String type, HttpServletResponse response) throws IOException { + taskTodoService.exportTaskDurationStat(startDate, endDate, type, response); + } + + @GetMapping("/getHistoryDevice") + @ApiOperation("获取历史点位") + public ResponseResult getHistoryDevice(Page> page, String startDate, String endDate, + String deviceId) { + Page> mapPage = taskTodoService.getHistoryDevice(page, startDate, endDate, deviceId); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/uploadTaskTodoById") + @ApiOperation("上传巡视报告") + public ResponseResult uploadTaskTodoById(String taskTodoId) throws Exception { + String[] splitIds = taskTodoId.split(","); + for (String splitId : splitIds) { + Map map = taskTodoService.getTaskTodoById(splitId, null, null, null); + if (map == null) { + continue; + } + String stationCode = map.get("stationCode").toString(); + List list = + substationService.list(new LambdaQueryWrapper().eq(Substation::getDatastatus, "1").eq(Substation::getStationCode, stationCode)); + if (list == null || list.size() == 0) { + continue; + } + Substation substation = list.get(0); + String stationId = substation.getStationId(); + // 获取环境信息 + Map weatherDevice = weatherLogService.getWeatherLogList(stationId); + map.put("environment", weatherDevice.get("environment")); + /* Map map = BeanUtil.beanToMap(taskTodo);*/ + map.put("stationName", substation.getStationName()); + map.put("stationType", substation.getStationType()); + map.put("voltLevel", substation.getVoltLevel()); + List> taskList = taskTodoService.getTaskResultById(map.get("taskTodoId").toString(), null, null, null); + List> abnormalList = + taskList.stream().filter(t -> !Objects.isNull(t.get("valid1")) && ("2").equals(t.get("valid1").toString())).collect(Collectors.toList()); + List> deviceByType = sysDictionaryItemsService.getDeviceByType("data_type"); + abnormalList.forEach(a -> { + if (!Objects.isNull(a.get("dataType"))) { + String meterType = a.get("dataType").toString(); + for (Map data : deviceByType) { + String itemcode = data.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + a.put("dataType", data.get("dictname")); + break; + } + } + } + a.put("index", abnormalList.indexOf(a) + 1); + a.put("status", "异常"); + // 图片流 + try { + String filePath = a.get("defectFilePath").toString(); + if (StrUtil.isNotBlank(filePath)) { + filePath = URLDecoder.decode(filePath, "utf-8"); + filePath = filePath.replace("\\", "/"); + log.info("报告图片路径" + httpServerConfig.getSnapFilePath() + filePath); + a.put("img", + Pictures.ofStream(new FileInputStream(new File(httpServerConfig.getSnapFilePath() + filePath)), + FileUtil.getPictureType(filePath)) + .size(100, 120).create()); + } + + } catch (Exception e) { + log.error(e.getMessage());; + } + }); + List> normalList = + taskList.stream().filter(t -> !Objects.isNull(t.get("valid1")) && ("1").equals(t.get("valid1").toString())).collect(Collectors.toList()); + normalList.forEach(n -> { + if (!Objects.isNull(n.get("dataType"))) { + String meterType = n.get("dataType").toString(); + for (Map data : deviceByType) { + String itemcode = data.get("itemcode").toString(); + if (meterType.equals(itemcode)) { + n.put("dataType", data.get("dictname")); + break; + } + } + } + n.put("index", normalList.indexOf(n) + 1); + n.put("status", "正常"); + try { + String filePath = n.get("defectFilePath").toString(); + if (StrUtil.isNotBlank(filePath)) { + filePath = URLDecoder.decode(filePath, "utf-8"); + filePath = filePath.replace("\\", "/"); + log.info("报告图片路径" + httpServerConfig.getSnapFilePath() + filePath); + n.put("img", + Pictures.ofStream(new FileInputStream(new File(httpServerConfig.getSnapFilePath() + filePath)), + FileUtil.getPictureType(filePath)) + .size(100, 120).create()); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + }); + List> demo = getMapData(); + //创建一个列表的规则 + HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy(); + //设置列表配置,如果有多个列表时需加.bind("list1", policy) 新列表配置即可 + Configure config = Configure.builder().bind("normalList", policy).bind("abnormalList", policy).build(); + if (abnormalList.size() > 0) { + map.put("abnormalList", abnormalList); + } else { + map.put("abnormalList", demo); + } + if (normalList.size() > 0) { + map.put("normalList", normalList); + } else { + map.put("normalList", demo); + } + + String reportName = "巡视报告模板.docx"; + // 加载模板渲染数据 + String path = Objects.requireNonNull(Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource( + "")).getPath() + "templates" + File.separator + reportName; + XWPFTemplate compile = XWPFTemplate.compile(path, config); + XWPFTemplate template = compile.render(map); + String taskName = map.get("taskName").toString(); + String replaceAll = taskName.replaceAll("[/.]", ""); + String startTime = map.get("startTime").toString(); + DateTime parse = DateUtil.parse(startTime); + String dateStr = DateUtil.format(parse, "yyyyMMDDHHmmss"); + String fileName = replaceAll + dateStr + ".docx"; + String filePath = httpServerConfig.getModelPath() + fileName; + template.write(new FileOutputStream(filePath)); + ftpClient.uploadFile(filePath, fileName); + } + + return ResponseResult.success(); + } + + @GetMapping("/getNonHomologousAnalysis") + @ApiOperation("查询非同源对比分析列表") + public ResponseResult getNonHomologousAnalysis(String stationCode, String taskName, String mainDeviceName, + String componentName, String recognitionType, String startDate, String endDate) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + List> mapList = taskTodoService.getNonHomologousAnalysis(stationCode, taskName, + mainDeviceName, componentName, recognitionType, startFormat, endFormat); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getNonCoherentAnalysis") + @ApiOperation("查询非同相对比分析列表") + public ResponseResult getNonCoherentAnalysis(String stationCode, String taskName, String mainDeviceName, + String componentName, String recognitionType, String startDate, + String endDate) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + List> mapList = taskTodoService.getNonCoherentAnalysis(stationCode, taskName, + mainDeviceName, componentName, recognitionType, startFormat, endFormat); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getTrendChangeDeviceList") + @ApiOperation("查询趋势变化点位列表") + public ResponseResult getTrendChangeDeviceList(Page> page, String stationCode, + String mainDeviceName, String componentName, + String recognitionType, String deviceName, String startDate, + String endDate) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + Page> mapPage = taskTodoService.getTrendChangeDeviceList(page, stationCode, mainDeviceName, componentName, recognitionType, deviceName, startFormat, endFormat); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/getTrendChangeAnalysisList") + @ApiOperation("查询趋势变化分析列表") + public ResponseResult getTrendChangeAnalysisList(String deviceId, String startDate, String endDate) { + String startFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + Map mapList = taskTodoService.getTrendChangeAnalysisList(deviceId, startFormat, + endFormat); + return ResponseResult.successData(mapList); + } + + @GetMapping("/getThreadTask") + @ApiOperation("测试查询多线程任务分配") + public ResponseResult getThreadTask(String taskTodoId) { + return taskTodoService.getThreadTask(taskTodoId); + } + + @PostMapping("/onceAnalyse") + @ApiOperation("单点识别") + public ResponseResult onceAnalyse(@RequestBody String param) throws Exception { + JSONObject jsonObject = JSONUtil.parseObj(param); + boolean isOk = taskTodoService.onceAnalyse(jsonObject.getStr("id")); + return ResponseResult.success(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/AlarmLog.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/AlarmLog.java new file mode 100644 index 0000000..ef1ef89 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/AlarmLog.java @@ -0,0 +1,266 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 告警日志表 + *

+ * + * + * @since 2023-05-03 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_alarm_log") +public class AlarmLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * uuid + */ + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 变电站id + */ + private String stationId; + + private String stationName; + + /** + * 和中台对应资源 ID 保持一致 + */ + private String stationCode; + + /** + * 和中台对应资源 ID 保持一致 + */ + private String areaId; + + /** + * 区域全名 + */ + private String areaName; + + /** + * 间隔ID + */ + private String bayId; + + /** + * 间隔名称 + */ + private String bayName; + + /** + * 主设备ID + */ + private String mainDeviceId; + + /** + * 主设备名称 + */ + private String mainDeviceName; + + /** + * 部件ID + */ + private String componentId; + + /** + * 部件名称 + */ + private String componentName; + + /** + * 设备点位名称 + */ + private String deviceName; + + /** + * 设备点位id + */ + private String deviceId; + + /** + * 巡视设备编码 + */ + private String patroldeviceCode; + + /** + * 巡视设备名称 + */ + private String patroldeviceName; + + /** + * 任务id + */ + private String taskId; + + /** + * 巡视任务执行ID + */ + private String taskPatrolledId; + + /** + * 巡视任务结果id + */ + private String taskResultId; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 任务编码 + */ + private String taskCode; + + /** + * 实物ID + */ + private String materialId; + + /** + * 1:巡视任务告警;2:静默任务告警,3:巡视设备告警 + */ + private String taskAlarmType; + + /** + * 字典项<1-100>: 行为类监视类型<101-200>: 环境类监视类型<201-300>: 设备类监视类型 + */ + private String monitorType; + + /** + * 缺陷类别 + */ + private String defectType; + + /** + * 1:预警;2:一般;3:严重;4;危急 + */ + private String alarmLevel; + + /** + * 字典表<1>: = 超温报警<2>: = 温升报警<3>: = 三相温差报警<4>: = 三相对比报警<5>: = 声音异常<6>: = 外观异常<7>: = 仪表越限报警<8>: = 仪表超量程报警<9>: = 仪表三相对比<10>: = 变位报警 + */ + private String alarmType; + + /** + * 1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6:闪烁检测11:局放超声波检测12:局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测 + */ + private String recognitionType; + + /** + * 1:红外图谱;2:可见光照片;3:音频4:视频5:识别图片,存在多个文件,文件类型用逗号分隔 + */ + private String fileType; + + /** + * 存在多个文件,路径用逗号分隔,并与文件类型一一对应 + */ + private String filePath; + + /** + * 存在多个文件,路径用逗号分隔,并与文件类型一一对应 + */ + private String defectFilePath; + + /** + * 值 + */ + private String value; + + /** + * 单位 + */ + private String unit; + + /** + * 告警描述 + */ + private String content; + + /** + * 发送报警时间 + */ + private String alarmDate; + + /** + * 0:未核查;1:已审核;2.已修正 + */ + private String checkFlag; + + /** + * 核查人编号 + */ + private String checkUserCode; + + /** + * 核查人名称 + */ + private String checkUserName; + + /** + * 核查时间 + */ + private String checkDate; + + /** + * 核查反馈意见 + */ + private String checkComment; + + /** + * 是否属实 + */ + private String checkResult; + + /** + * 格式:x1,y1,x2y2;x3,y3,x4,y4;多个报警框的左上角和右下角坐标 + */ + private String rectangle; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/DefectDevice.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/DefectDevice.java new file mode 100644 index 0000000..14b3b75 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/DefectDevice.java @@ -0,0 +1,290 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author zhengsl + * @since 2024-03-28 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_defect_device") +public class DefectDevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 缺陷库id + */ + @TableId(value = "defect_id", type = IdType.ASSIGN_UUID) + private String defectId; + + /** + * 是否归入缺陷库,0:未归入;1:归入;默认0 + */ + private String isDefectFlag; + + /** + * 巡视结果id + */ + private String resultId; + + /** + * 巡视任务执行ID + */ + private String taskTodoId; + + /** + * 巡视点位顺序号 + */ + private Integer orderNum; + + /** + * 巡视设备编号 + */ + private String patroldeviceCode; + + /** + * 巡视设备名称 + */ + private String patroldeviceName; + + /** + * 巡视设备对应通道编号 + */ + private String patroldeviceChannelcode; + + /** + * 巡视设备名称 + */ + private String patroldevicePos; + + /** + * 判别用基准图 + */ + private String patroldeviceBaseimage; + + /** + * 辅助识别的基准图有限区域 + */ + private String patroldeviceEffectiveregion; + + /** + * 巡视日期 + */ + private String patroldeviceDate; + + /** + * 任务编码 + */ + private String taskCode; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 部件ID + */ + private String componentId; + + /** + * 部件名称 + */ + private String componentName; + + /** + * 区域id + */ + private String areaId; + + /** + * 区域名称 + */ + private String areaName; + + /** + * 间隔ID + */ + private String bayId; + + /** + * 间隔名称 + */ + private String bayName; + + /** + * 主设备ID + */ + private String mainDeviceId; + + /** + * 主设备名称 + */ + private String mainDeviceName; + + /** + * 设备点位id + */ + private String deviceId; + + /** + * 设备点位名称 + */ + private String deviceName; + + /** + * 实物ID + */ + private String materialId; + + /** + * 0x01视频设备,0x02机器人,0x03无人机,0x04声纹装置,0x05在线检测 + */ + private String dataType; + + /** + * 识别类型:1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6:闪烁检测11:局放超声波检测12:局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测 + */ + private String recognitionType; + + /** + * 1:红外图谱;2:可见光照片;3:音频4:视频5:识别图片,存在多个文件,文件类型用逗号分隔 + */ + private String fileType; + + /** + * 存在多个文件,路径用逗号分隔,并与文件类型一一对应 + */ + private String filePath; + + /** + * 图像识别结果类型:meter 仪表读数 + */ + private String valueType; + + /** + * 值 + */ + private String value; + + /** + * 单位 + */ + private String unit; + + /** + * 识别结果 + */ + @TableField("`desc`") + private String desc; + + /** + * 置信度 + */ + private String conf; + + /** + * 时间 + */ + private String time; + + /** + * 检测标注图片 + */ + private String defectFilePath; + + /** + * 格式:x1,y1;x2,y2;x3,y3;x4,y4等为图片文件的像素点;多个报警框的左上角和右下角坐标 + */ + private String rectangle; + + /** + * 0未巡视, 1:已巡视(图像已采集),2:已完成(图像已分析); 3:设备检修中; 4:巡视失败; 5:已修正 6:采集失败; + */ + private String flag; + + /** + * 0为失败,1为成功,2为判别异常 + */ + private String valid; + + /** + * 修正值 + */ + private String reviseValue; + + /** + * 修正单位 + */ + private String reviseUnit; + + /** + * 修正结果,0:正常;1:异常 + */ + private String reviseValid; + + /** + * 0:否;1:是 + */ + private String isAlarm; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + private LocalDateTime lastmodifydate; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 机器人/无人机编码 + */ + private String robotCode; + + /** + * 机器人识别结果 + */ + private String robotResult; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/ExaminePlan.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/ExaminePlan.java new file mode 100644 index 0000000..4782e32 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/ExaminePlan.java @@ -0,0 +1,170 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 检修计划表 + *

+ * + * + * @since 2023-04-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_examine_plan") +@ApiModel("检修计划") +public class ExaminePlan implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_UUID) + @ApiModelProperty("检修计划id") + private String id; + + /** + * config_name(检修计划名称) + */ + @ApiModelProperty("检修计划id") + private String configName; + /** + * 变电站id + */ + @ApiModelProperty("变电站id") + private String stationId; + + /** + * 变电站编码 + */ + @ApiModelProperty("变电站编码") + private String stationCode; + + /** + * 变电站名称 + */ + @ApiModelProperty("变电站名称") + private String stationName; + + /** + * 检修区域配置编码 + */ + @ApiModelProperty("检修区域配置编码") + private String configCode; + + /** + * 1:间隔;2:主设备;3设备点位;4:部件 + */ + @ApiModelProperty("1:间隔;2:主设备;3设备点位;4:部件") + private Integer deviceLevel; + + /** + * 多个id,以','分隔 + */ + @ApiModelProperty("多个id,以','分隔") + private String deviceList; + + /** + * 1:设置检修,0:取消检修 + */ + @ApiModelProperty("1:设置检修,0:取消检修") + private Integer enable; + + /** + * 检修开始时间 + */ + @ApiModelProperty("检修开始时间") + private String startTime; + + /** + * 检修结束时间 + */ + @ApiModelProperty("检修结束时间") + private String endTime; + + /** + * 坐标框(像素点) + */ + @ApiModelProperty("坐标框(像素点)") + private String coordinatePixel; + + /** + * 检修备注 + */ + @ApiModelProperty("检修备注") + private String examineComment; + + /** + * 编制人编号 + */ + @ApiModelProperty("编制人编号") + private String creatorcode; + + /** + * 编制人 + */ + @ApiModelProperty("编制人") + private String creator; + + /** + * 编制时间 + */ + @ApiModelProperty("编制时间") + private String createTime; + + /** + * 数据状态(绑定状态) + */ + @ApiModelProperty("数据状态") + private String datastatus; + + /** + * 修改人 + */ + @ApiModelProperty("修改人") + private String lastmodifier; + + /** + * 最近修改时间 + */ + @ApiModelProperty("最近修改时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + @ApiModelProperty("备用1") + private String custom1; + + /** + * 备用2 + */ + @ApiModelProperty("备用2") + private String custom2; + + /** + * 备用3 + */ + @ApiModelProperty("备用3") + private String custom3; + + /** + * 机器人编号 + */ + @ApiModelProperty("机器人编号") + private String robotCode; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/Task.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/Task.java new file mode 100644 index 0000000..46d43c0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/Task.java @@ -0,0 +1,242 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 巡视任务表 + *

+ * + * + * @since 2023-04-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_task") +public class Task implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "task_id", type = IdType.ASSIGN_UUID) + private String taskId; + + private String stationCode; + + private String stationName; + + /** + * 1:例行巡视;2:特殊巡视;3:专项巡视;4:自定义巡视 + */ + private String type; + + /** + * 任务编码 + */ + private String taskCode; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 1:站端日常巡视任务;2:上级系统控制;3区域巡视系统控制,4主辅设备联动 + */ + private String taskType; + + /** + * 任务执行方式,1立即执行,2定时执行,3周期执行 + */ + private String taskTodoType; + + /** + * 1:优先级1,优先级最低;2:优先级2;3:优先级3;4:优先级4,优先级最高 + */ + private String priority; + + /** + * 1:间隔;2:主设备;3设备点位;4:部件 + */ + private Integer deviceLevel; + + /** + * 格式:多个ID,采用','分隔;静默任务时只能选点位或者选摄像机 + */ + private String deviceList; + + /** + * 静默任务时,选择静默识别缺陷类型,采用','分隔,设备列表选择点位时不需要此项 + */ + //private String defectType; + + /** + * 定期开始时间 + */ + private String fixedStartTime; + + /** + * 格式:多个月,采用","分隔,例如 1,2,5,代表一月,二月,五月 + */ + private String cycleMonth; + + /** + * 格式:一周中多天执行,采用","分隔;例如 1,2,7,代表周一,周二,周日 + */ + private String cycleWeek; + + /** + * 格式:HH:mm:ss + */ + private String cycleExecuteTime; + + /** + * 周期开始时间 + */ + private String cycleStartTime; + + /** + * 周期结束时间 + */ + private String cycleEndTime; + + /** + * 间隔(数量) + */ + private String intervalNumber; + + /** + * 1:小时;2:天 + */ + private String intervalType; + + /** + * 格式HH:mm:ss + */ + private String intervalExecuteTime; + + /** + * 间隔开始时间 + */ + private String intervalStartTime; + + /** + * 间隔结束时间 + */ + private String intervalEndTime; + + /** + * 不可用开始时间 + */ + private String invalidStartTime; + + /** + * 不可用结束时间 + */ + private String invalidEndTime; + + /** + * 是否可用;0:不可用;1:可用 + */ + private String isenable; + + /** + * cron表达式 + */ + private String cronValue; + + /** + * 编制人编号 + */ + private String creatorcode; + + /** + * 编制人 + */ + private String creator; + + /** + * 编制时间 + */ + private String createTime; + + /** + * 任务巡视累计次数,每次巡视执行任务执行完毕进行回写 + */ + private Integer taskNumberTotal; + + /** + * 数据状态,1:未复核;2:已复核;3:已删除 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 是否上报;默认0:未上报;1:已上报 + */ + @TableField("is_report") + private String isReport; + + /** + * 监控索引号为监控系统测点 ID,可用于数据的唯一标识 + */ + private String controlNum; + + /** + * 机器人/无人机编码 + */ + private String robotCode; + + /** + * 任务类别 1-摄像机任务 2-机器人任务 3-无人机 + */ + private String taskClass; + + /** + * 机器人或者无人绑定的线路ID + */ + private String taskLineid; + + /** + * 无人机机巢信息 + */ + private String dockInfo; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskDolog.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskDolog.java new file mode 100644 index 0000000..850a76a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskDolog.java @@ -0,0 +1,106 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 巡视任务执行日志 + *

+ * + * + * @since 2023-05-03 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_task_dolog") +public class TaskDolog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.ASSIGN_UUID) + private String id; + + /** + * 任务名称 + */ + private String stationCode; + + /** + * 任务编码 + */ + private String taskCode; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 执行任务ID + */ + private String taskTodoid; + + /** + * 操作类型: 系统执行、系统中断、手动暂停、手动恢复、手动终止 + */ + private String doType; + + /** + * 计划开始时间 + */ + private String planStartTime; + + /** + * 实际开始时间 + */ + private String doStartTime; + + /** + * 任务执行时间 单位秒 + */ + private String doTotalTime; + + /** + * 日志描述 + */ + private String description; + + /** + * 日志时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp logTime; + + /** + * 操作人 + */ + private String logger; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskResult.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskResult.java new file mode 100644 index 0000000..331cb71 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskResult.java @@ -0,0 +1,295 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 巡视任务结果表 + *

+ * + * + * @since 2023-04-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_task_result") +public class TaskResult implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 巡视结果id + */ + @TableId(value = "result_id", type = IdType.ASSIGN_UUID) + private String resultId; + + /** + * 巡视任务执行ID + */ + private String taskTodoId; + + /** + * 巡视点位顺序号 + */ + private Integer orderNum; + + /** + * 巡视设备-对应机器人/无人机编码 + */ + private String robotCode; + /** + * 机器人结果 + */ + private String robotResult; + + /** + * 巡视设备编号 + */ + private String patroldeviceCode; + + /** + * 巡视设备对应通道编号 + */ + private String patroldeviceChannelcode; + + /** + * 巡视设备名称 + */ + private String patroldeviceName; + + + /** + * 巡视设备预置位 + */ + private String patroldevicePos; + + + /** + * 巡视预置位基准图 + */ + private String patroldeviceBaseimage; + + + + /** + * 巡视预置位基准图 + */ + private String patroldeviceEffectiveregion; + + + /** + * 任务编码 + */ + private String taskCode; + + + /** + * 任务名称 + */ + private String taskName; + + /** + * 部件ID + */ + private String componentId; + + /** + * 部件名称 + */ + private String componentName; + + /** + * 区域id + */ + private String areaId; + + /** + * 区域名称 + */ + private String areaName; + + /** + * 间隔ID + */ + private String bayId; + + /** + * 间隔名称 + */ + private String bayName; + + /** + * 主设备ID + */ + private String mainDeviceId; + + /** + * 主设备名称 + */ + private String mainDeviceName; + + /** + * 设备点位名称 + */ + private String deviceName; + + /** + * 设备点位id + */ + private String deviceId; + + + + /** + * 实物ID + */ + private String materialId; + + /** + * 值类型 + */ + private String valueType; + + + /** + * 值 + */ + @TableField("`value`") + private String value; + + /** + * 单位 + */ + private String unit; + + + + /** + * 识别结果描述 + */ + @TableField("`desc`") + private String desc; + + /** + * 识别结果置信度 + */ + private String conf; + + /** + * 时间 + */ + private String time; + + /** + * 1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6:闪烁检测11:局放超声波检测12:局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测 + */ + private String recognitionType; + + /** + * 1:红外图谱;2:可见光照片;3:音频 4:视频 5:识别图片,存在多个文件,文件类型用逗号分隔 + */ + private String fileType; + + /** + * 存在多个文件,路径用逗号分隔,并与文件类型一一对应 + */ + private String filePath; + + /** + * 识别后加上了标识的图片地址 + */ + private String defectFilePath; + + /** + * 格式:x1,y1;x2,y2;x3,y3;x4,y4等为图片文件的像素点;多个报警框的左上角和右下角坐标 + */ + private String rectangle; + + /** + * 0x01视频设备,0x02机器人,0x03无人机,0x04声纹装置,0x05在线检测 + */ + private String dataType; + + /** + * 0未巡视, 1:已巡视(图像已采集),2:已完成(图像已分析); 3:设备检修中; 4:巡视失败; 5:已修正 6:采集失败; + */ + private String flag; + + /** + * 巡视时间 + */ + private String patroldeviceDate; + + /** + * 0为失败,1为成功,2为判别异常 + */ + private String valid; + + /** + * 修正值 + */ + private String reviseValue; + + /** + * 修正单位 + */ + private String reviseUnit; + + /** + * 修正结果,0:正常;1:异常 + */ + private String reviseValid; + + /** + * 0:否;1:是 + */ + private String isAlarm; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 变电站代码 + */ + private String stationCode; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskTodo.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskTodo.java new file mode 100644 index 0000000..51c52ba --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/domain/TaskTodo.java @@ -0,0 +1,217 @@ +package com.yfd.platform.modules.patroltask.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 巡视任务执行表 + *

+ * + * + * @since 2023-04-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_task_todo") +public class TaskTodo implements Serializable { + + public static final String JOB_KEY = "JOB_KEY"; + private static final long serialVersionUID = 1L; + + /** + * 巡视任务执行ID + */ + @TableId(value = "task_todo_id", type = IdType.ASSIGN_UUID) + private String taskTodoId; + + /** + * 变电站代码 + */ + //private String id; + private String stationCode; + + /** + * 巡视类型 + */ + private String type; + + /** + * 任务id + */ + private String taskId; + + /** + * 任务名称 + */ + private String taskName; + + /** + * 任务编码 + */ + private String taskCode; + + /** + * 1:站端日常巡视任务;2:上级系统控制;3区域巡视系统控制,4主辅设备联动 + */ + private String taskType; + + /** + * 1:优先级1,优先级最低;2:优先级2;3:优先级3;4:优先级4,优先级最高 + */ + private String priority; + + /** + * 1:已执行;2:正在执行;3:暂停;4:终止;5:未执行;6:超期; + */ + private String taskState; + + /** + * 计划开始时间 + */ + private String planStartTime; + + /** + * 开始时间 + */ + private String startTime; + + /** + * 任务进度 + */ + private String taskProgress; + + /** + * 任务预计剩余时间 + */ + private String taskEstimatedTime; + + /** + * 结束时间 + */ + private String endTime; + + /** + * 巡视结果描述 + */ + private String description; + + /** + * 当前巡检点位id + */ + private String deviceNow; + + /** + * 巡检点位总数 + */ + private Integer deviceSumnum; + + /** + * 已识别点数(包括识别失败 识别正常) + */ + private Integer deviceDeforeNum; + + /** + * 异常巡检点位总数 + */ + private Integer deviceUnusualnum; + + /** + * 巡视失败点位数 + */ + private Integer deviceFailureNum; + + /** + * 0:未审核;1:已审核 + */ + private String examineFlag; + + /** + * 审核人编号 + */ + private String cexamineUserCode; + + /** + * 审核人名称 + */ + private String cexamineUserName; + + /** + * 审核时间 + */ + private String examineDate; + + /** + * 审核描述 + */ + private String examineComment; + + /** + * 数据状态 + */ + private String datastatus; + + /** + * 修改人 + */ + private String lastmodifier; + + /** + * 最近修改时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 巡视路线,当机器人或无人机生成巡视路线时,该字段进行回写,json格式包含多个坐标和经纬度 + */ + private String taskTodoWay; + + /** + * 监控索引号为监控系统测点 ID,可用于数据的唯一标识 + */ + private String controlNum; + + /** + * 机器人/无人机编码 + */ + private String robotCode; + + /** + * 任务类别 1-摄像机任务 2-机器人任务 3-无人机 + */ + private String taskClass; + + /** + * 机器人或者无人绑定的线路ID + */ + private String taskLineid; + + /** + * 无人机机巢信息 + */ + private String dockInfo; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/AlarmLogMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/AlarmLogMapper.java new file mode 100644 index 0000000..399f502 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/AlarmLogMapper.java @@ -0,0 +1,41 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; + +import java.util.Map; + +/** + *

+ * 告警日志表 Mapper 接口 + *

+ * + * + * @since 2023-05-03 + */ +public interface AlarmLogMapper extends BaseMapper { + + /********************************** + * 用途说明: 静默监视查询 + * 参数说明 stationId + * 参数说明 areaId + * 参数说明 patrolDeviceName + * 参数说明 monitorType + * 返回值说明: java.util.List> + ***********************************/ + Page> getAlarmListByType(Page> page, String stationId, String areaId, + String patrolDeviceName, String monitorType); + + /********************************** + * 用途说明: 根据设备编号查询告警列表 + * 参数说明 page + * 参数说明 patrolDeviceCode + * 参数说明 monitorType + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getAlarmListPage(Page> page, String patrolDeviceCode, String monitorType, String startDate, String endDate); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/DefectDeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/DefectDeviceMapper.java new file mode 100644 index 0000000..7386da2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/DefectDeviceMapper.java @@ -0,0 +1,21 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.patroltask.domain.DefectDevice; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import java.util.Map; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zhengsl + * @since 2024-03-28 + */ +public interface DefectDeviceMapper extends BaseMapper { + + Page> getDefectDeviceList(Page> page, String stationCode, String areaId, String bayId, String taskName, String deviceName, String patroldeviceName, String componentName, String recognitionType, String isDefectFlag, String startDate, String endDate); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/ExaminePlanMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/ExaminePlanMapper.java new file mode 100644 index 0000000..d98e5a1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/ExaminePlanMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; + +/** + *

+ * 检修计划表 Mapper 接口 + *

+ * + * + * @since 2023-04-08 + */ +public interface ExaminePlanMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskDologMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskDologMapper.java new file mode 100644 index 0000000..908b748 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskDologMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.modules.patroltask.domain.TaskDolog; + +/** + *

+ * 巡视任务执行日志 Mapper 接口 + *

+ * + * + * @since 2023-05-03 + */ +public interface TaskDologMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskMapper.java new file mode 100644 index 0000000..4ff1a94 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskMapper.java @@ -0,0 +1,87 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.patroltask.domain.Task; +import org.apache.ibatis.annotations.Insert; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 巡视任务表 Mapper 接口 + *

+ * + * + * @since 2023-04-08 + */ +public interface TaskMapper extends BaseMapper { + + Page> getTaskList(Page> page, String startDate, String endDate, + String type, String taskName, String enable, String taskType, + String taskTodoType); + + @Insert("") + void batchAdd(List taskList); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskResultMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskResultMapper.java new file mode 100644 index 0000000..0a4c716 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskResultMapper.java @@ -0,0 +1,208 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import org.apache.ibatis.annotations.Insert; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 巡视任务结果表 Mapper 接口 + *

+ * + * + * @since 2023-04-08 + */ +public interface TaskResultMapper extends BaseMapper { + + /********************************** + * 用途说明: 异常点位查询 + * 参数说明 page + * 参数说明 taskTodoIds + * 参数说明 taskName + * 参数说明 deviceName + * 参数说明 patroldeviceName + * 参数说明 componentName + * 参数说明 recognitionType + * 参数说明 status + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getAbnormalDeviceList(Page> page, String stationCode,String areaId, + String bayId, String taskName, + String deviceName, String patroldeviceName, + String componentName, String recognitionType, String status, + String startDate, String endDate); + + Page> getTaskDeviceList(Page> page, String taskTodoId, String deviceName, + String valid); + + /********************************** + * 用途说明: 查询点位历史曲线 + * 参数说明 deviceId + * 返回值说明: java.util.List> + ***********************************/ + List> getHistoricalCurve(String deviceId); + + /********************************** + * 用途说明: 根据任务执行情况查询任务结果 + * 参数说明 taskTodoId 任务执行情况id + * 返回值说明: java.util.List + ***********************************/ + List> getTaskResultById(String taskTodoId,String areaId,String bayId,String mainDeviceId); + + /********************************** + * 用途说明: 导出异常点位(导出全部) + * 参数说明 page + * 参数说明 taskTodoId + * 参数说明 valid + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getAbnormalDevice(String stationCode, String taskName, String deviceName, String patroldeviceName, String componentName, String recognitionType, String status, String startDate, String endDate); + + /********************************** + * 用途说明: 根据任务执行情况ID查询点位查询 + * 参数说明 taskTodoId + * 参数说明 deviceName + * 参数说明 valid + * 返回值说明: java.util.List> + ***********************************/ + List> getTaskDeviceById(String taskTodoId, String deviceName, String valid); + + + /********************************** + * 用途说明: 巡视设备巡检统计 + * 参数说明 page + * 参数说明 patrolDeviceCode + * 参数说明 patrolDeviceName + * 参数说明 type + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getDeviceInspectionStat(Page> page, String patrolDeviceCode, String patrolDeviceName, String type); + + /********************************** + * 用途说明: 巡视任务数据统计 + * 参数说明 page + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTaskDataStat(Page> page, String stationCode, String type, + String startDate, String endDate); + + /********************************** + * 用途说明: 巡视任务数据统计(不分页) + * 参数说明 page + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getTaskDataStat(String stationCode, String type, String startDate, String endDate); + + /********************************** + * 用途说明: 根据任务id查询未审核告警和数量 + * 参数说明 taskTodoIds + * 返回值说明: java.util.List> + ***********************************/ + List> getNotCheckAlarmInfo(String taskTodoIds); + + @Insert("") + void batchAdd(List taskList); + + List> getPatrolDeviceInspectionStat(); + + List> getNonHomologousAnalysis(String stationCode, String taskName, String mainDeviceId, String componentId, String bayId, String startFormat, String endFormat); + + List> getNonCoherentAnalysis(String taskTodoId, String stationCode, String taskName, + String mainDeviceName, String componentName, + String recognitionType, String startDate, String endDate); + + List getAbnormalResult(String taskTodoId); + + List> getValueByDeviceId(String deviceId); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskTodoMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskTodoMapper.java new file mode 100644 index 0000000..ca7ee75 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/mapper/TaskTodoMapper.java @@ -0,0 +1,132 @@ +package com.yfd.platform.modules.patroltask.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 巡视任务执行表 Mapper 接口 + *

+ * + * + * @since 2023-04-08 + */ +public interface TaskTodoMapper extends BaseMapper { + + Page> getTaskImplementation(Page> page, String stationCode, + String currentDate, String startDate, String endDate,String taskState,String taskName); + + @Select("select a.station_code,a.result_id,a.flag,a.task_todo_id,a.bay_name,a.main_device_id,a.main_device_name,a" + + ".device_id,a.device_name,a.datastatus,a.file_type,a.patroldevice_code,a.patroldevice_code rtsp_device_code,a.patroldevice_channelcode,a.patroldevice_channelcode rtsp_channel_code,a.patroldevice_pos,a.patroldevice_pos rtsp_channel_pos,a.patroldevice_baseimage,a" + + ".patroldevice_effectiveregion,a.custom1," + + "b.patroldevice_json,b.recognition_type_name_list,b.identify_material_id,b.outside_angle,b.meter_type,b" + + ".outside_feature,b.picture_analysis_type_list,b.effective_area " + + " from iis_task_result a join iis_substation_device b on (a.device_id=b.device_id)" + + " where a.task_todo_id=#{taskTodoId}" + + " order by a.order_num" + ) + List> queryTaskResult(String taskTodoId); + + List> getTaskToDoStatList(String stationCode, String currentDate, String startDate, + String endDate,String taskName); + + Page> getTaskReportList(Page> page, String stationCode, String areaId, + String taskName, String type, String startDate, String endDate, + String bayId, String patrolType); + + /********************************** + * 用途说明: 查询主设备所关联的点位 + * 参数说明 ids + * 参数说明 meterType + * 参数说明 recognitionType + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: java.util.List> + ***********************************/ + List> getDeviceByMainDevice(String mainDeviceId, String meterType, String recognitionType, + String startDate, String endDate); + + Page> getLinkTaskTodoList(Page> page, String stationCode, + String linkageDeviceName, String taskName, String linkageType, + String linkageDeviceType, String startDate, String endDate); + + @Insert("") + void batchAdd(List taskList); + + List selectTaskTodoList(); + + /********************************** + * 用途说明: 查询联动任务最近十条 + * 参数说明 linkageType 联动类型 + * 返回值说明: java.util.List> + ***********************************/ + List> getLinkTaskRecordLast(String stationCode, String linkageType,String taskName); + + /********************************** + * 用途说明: 查询联动控制巡视任务 + * 参数说明 stationCode + * 参数说明 todoId + * 参数说明 linkageType + * 返回值说明: java.util.List> + ***********************************/ + List> getLinkTaskTodo(String stationCode, String todoId, String linkageType); + + List> getTaskDurationStat(String startFormat, String endFormat, String type); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/DockTaskService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/DockTaskService.java new file mode 100644 index 0000000..0e33483 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/DockTaskService.java @@ -0,0 +1,25 @@ +package com.yfd.platform.modules.patroltask.service; + +import com.yfd.platform.config.ResponseResult; + +import java.io.UnsupportedEncodingException; + +/** + * @Author pcj + * @Date 2024/7/5 13:53 + * @Version 1.0 + */ +public interface DockTaskService { + + /********************************** + * 用途说明: 保存无人机文件 + * 参数说明 jobId + * 参数说明 fileName + * 参数说明 url + * 返回值说明: void + ***********************************/ + ResponseResult saveMediaFile(String jobId, String fileName, String url) throws Exception; + + void sendTaskResultData(String taskTodoId) throws Exception; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IAlarmLogService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IAlarmLogService.java new file mode 100644 index 0000000..10f4460 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IAlarmLogService.java @@ -0,0 +1,60 @@ +package com.yfd.platform.modules.patroltask.service; + +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; + +import java.io.UnsupportedEncodingException; +import java.util.Map; + +/** + *

+ * 告警日志表 服务类 + *

+ * + * + * @since 2023-05-03 + */ +public interface IAlarmLogService extends IService { + + void sendNonCoherentMsg1(TaskResult taskResult, double alertValue) throws Exception; + + boolean updateTaskResult(JSONObject jsonObject) throws Exception; + + //静默任务返回报警记录 + boolean createAlarm(JSONObject jsonObject) throws Exception; + + /********************************** + * 用途说明: 静默监视查询 + * 参数说明 stationId + * 参数说明 areaId + * 参数说明 patrolDeviceName + * 参数说明 monitorType + * 返回值说明: java.util.List> + ***********************************/ + Page> getAlarmListByType(Page> page, String stationId, String areaId, + String patrolDeviceName, String monitorType); + + /********************************** + * 用途说明: 根据设备编号查询告警列表 + * 参数说明 page + * 参数说明 patrolDeviceCode + * 参数说明 monitorType + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getAlarmListPage(Page> page, String patrolDeviceCode, String monitorType, String startDate, String endDate); + + Page> getAlarmLogPage(Page> page, String stationCode, String taskAlarmType, + String alarmLevel, String checkFlag, String startDate, String endDate); + + boolean setAlarmLogStatus(AlarmLog alarmLog); + + Map getAlarmLogById(String id); + + boolean updateDeviceResult(JSONObject jsonObject) throws Exception; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IDefectDeviceService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IDefectDeviceService.java new file mode 100644 index 0000000..c694992 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IDefectDeviceService.java @@ -0,0 +1,58 @@ +package com.yfd.platform.modules.patroltask.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.patroltask.domain.DefectDevice; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; + +/** + *

+ * 服务类 + *

+ * + * @author zhengsl + * @since 2024-03-28 + */ +public interface IDefectDeviceService extends IService { + + /********************************** + * 用途说明: 查询缺陷数据 + * 参数说明 page + * 参数说明 stationId 变电站id + * 参数说明 areaId 区域id + * 参数说明 bayId 间隔id + * 参数说明 taskName 任务名称 + * 参数说明 deviceName 点位名称 + * 参数说明 patroldeviceName 设备名称 + * 参数说明 componentName 部件名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 isDefectFlag 是否归入 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + ResponseResult getDefectDeviceList(Page> page, String stationCode, String areaId, String bayId, String taskName, String deviceName, String patroldeviceName, String componentName, String recognitionType, String isDefectFlag, String startDate, String endDate); + + /********************************** + * 用途说明: 缺陷信息归入 + * 参数说明 ids id集合 + * 返回值说明: boolean + ***********************************/ + boolean setDefectDevice(String ids); + + /********************************** + * 用途说明: 根据id获取缺陷信息 + * 参数说明 id id + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + ResponseResult getDefectDeviceById(String id); + + /********************************** + * 用途说明: 缺陷信息删除 + * 参数说明 ids id集合 + * 返回值说明: boolean + ***********************************/ + boolean deleteDefectDevice(String ids); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IExaminePlanService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IExaminePlanService.java new file mode 100644 index 0000000..388dc04 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/IExaminePlanService.java @@ -0,0 +1,34 @@ +package com.yfd.platform.modules.patroltask.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 检修计划表 服务类 + *

+ * + * + * @since 2023-04-08 + */ +public interface IExaminePlanService extends IService { + + /********************************** + * 用途说明: 根据Id查询关联信息 + * 参数说明 id + * 参数说明 deviceLevel + * 返回值说明: java.util.List> + ***********************************/ + List> getBindInfoById(String id, String deviceLevel); + + /********************************** + * 用途说明: 根据Id查询检修计划绑定点位 + * 参数说明 id 检修计划id + * 返回值说明: java.util.List + ***********************************/ + List getBindExamineDevice(String id); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskDologService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskDologService.java new file mode 100644 index 0000000..94337d8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskDologService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.patroltask.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.patroltask.domain.TaskDolog; + +/** + *

+ * 巡视任务执行日志 服务类 + *

+ * + * + * @since 2023-05-03 + */ +public interface ITaskDologService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskService.java new file mode 100644 index 0000000..a258f07 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskService.java @@ -0,0 +1,87 @@ +package com.yfd.platform.modules.patroltask.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; + +import java.text.ParseException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 巡视任务表 服务类 + *

+ * + * + * @since 2023-04-08 + */ +public interface ITaskService extends IService { + + /********************************** + * 用途说明: 根据变电站Id查询巡视任务 + * 参数说明 stationId + * 返回值说明: java.util.List + ***********************************/ + List getTaskByStationId(String stationId); + + /********************************** + * 用途说明: 根据Id查询关联信息 + * 参数说明 taskId 任务id + * 参数说明 deviceLevel 任务类型 + * 返回值说明: java.util.List> + ***********************************/ + List> getBindInfoById(String id, String deviceLevel); + + /********************************** + * 用途说明: 新增巡视任务 + * 参数说明 task 巡视任务 + * 返回值说明: boolean + ***********************************/ + boolean addTask(Task task); + + /********************************** + * 用途说明: 分页查询巡视任务 + * 参数说明 page 分页对象 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 参数说明 type 巡视类型 + * 参数说明 taskName 任务名称 + * 参数说明 enable 是否启用 + * 参数说明 taskType 任务类型 + * 参数说明 taskTodoType 任务执行方式 + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTaskList(Page> page, String startDate, String endDate, + String type, String taskName, String enable, String taskType, String taskTodoType) throws ParseException; + + /********************************** + * 用途说明: 根据任务向数据库中插入数据(立即执行) + * 参数说明 task + * 返回值说明: void + ***********************************/ + TaskTodo createRunNowTask(Task task) throws ParseException; + + /********************************** + * 用途说明: 根据任务向数据库中插入数据(计划执行) + * 参数说明 task + * 返回值说明: TaskTodo + ***********************************/ + String createTodoTaskList(Task task) throws ParseException; + + /********************************** + * 用途说明: 向后创建一天的任务 + * 参数说明 task + * 返回值说明: TaskTodo + ***********************************/ + void createOnceTodoTask(String taskId) throws ParseException; + + /********************************** + * 用途说明: 根据任务向数据库中插入数据 + * 参数说明 task + * 返回值说明: void + ***********************************/ + void deleteTodoTasks(Task task) throws ParseException; +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskTodoService.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskTodoService.java new file mode 100644 index 0000000..83a522c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/ITaskTodoService.java @@ -0,0 +1,479 @@ +package com.yfd.platform.modules.patroltask.service; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.patroltask.domain.TaskDolog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 巡视任务执行表 服务类 + *

+ * + * + * @since 2023-04-08 + */ +public interface ITaskTodoService extends IService { + + /********************************** + * 用途说明: 查询巡视任务执行情况详情 + * 参数说明 taskTodoId + * 返回值说明: java.util.Map + ***********************************/ + Map getTaskToDo(String taskTodoId); + + /********************************** + * 用途说明: 分页查询巡视任务执行情况(前台) + * 参数说明 page + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTaskImplementation(Page> page, String stationCode, + String currentDate, String startDate, String endDate, String taskState, String taskName); + + TaskTodo doCurrentTask(String taskTodoId,String optType) throws InterruptedException; + + List> queryTaskResult(String taskTodoId); + + int queryExaminePlan(String deviceid, String planStartTime); + + boolean updateTaskResultStatus(String todotaskid, String taskresultid, String imagefilename, String flag); + + boolean updateTaskResult(TaskResult taskResult); + + boolean pauseLowerTask(String stationcode, String todotaskid, String priority); + + void sendTaskResult(TaskTodo taskTodo, TaskResult taskResult); + + /********************************** + * 用途说明: 任务开始前,判断是否有任务等级大于等于当前任务的,有就暂停当前任务 + * 参数说明 stationCode + * 参数说明 taskTodoId + * 参数说明 priority + * 返回值说明: boolean + ***********************************/ + boolean pauseUpperTask(String stationCode, String taskTodoId, String priority); + + boolean resumeLowerTask(String stationcode, String todotaskid, String priority,String custom3) throws InterruptedException; + + boolean recordTaskDoLog(TaskDolog taskDolog); + + /********************************** + * 用途说明: 查询巡视任务执行情况统计(前台) + * 参数说明 page + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getTaskToDoStatList(String stationCode, String currentDate, String startDate, + String endDate, String taskName); + + /********************************** + * 用途说明: 分页查询巡视报告 + * 参数说明 stationCode 变电站编号 + * 参数说明 type 巡视类型 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 返回值说明: java.util.List> + ***********************************/ + Page> getTaskReportList(Page> page, String stationCode, String areaId, + String taskName, String type, String startDate, String endDate, + String bayId, String patrolType); + + /********************************** + * 用途说明: 根据任务执行情况查询任务结果 + * 参数说明 taskTodoId 任务执行情况id + * 返回值说明: java.util.List + ***********************************/ + List> getTaskResultById(String taskTodoId,String areaId,String bayId,String mainDeviceId); + + /********************************** + * 用途说明: 异常点位查询 + * 参数说明 page + * 参数说明 taskTodoIds + * 参数说明 taskName + * 参数说明 deviceName + * 参数说明 patroldeviceName + * 参数说明 componentName + * 参数说明 recognitionType + * 参数说明 status + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getAbnormalDeviceList(Page> page, String stationCode, String areaId, + String bayId, + String taskName, + String deviceName, String patroldeviceName, + String componentName, String recognitionType, String status, + String startDate, String endDate); + + /********************************** + * 用途说明: 导出异常点位 + * 参数说明 records + * 参数说明 response + * 返回值说明: void + ***********************************/ + void exportAbnormalDevice(List> records, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 根据任务执行情况ID查询点位查询 + * 参数说明 page + * 参数说明 taskTodoId + * 参数说明 valid + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTaskDeviceList(Page> page, String taskTodoId, String deviceName, String valid); + + /********************************** + * 用途说明: 查询点位历史曲线 + * 参数说明 deviceId + * 返回值说明: java.util.List> + ***********************************/ + List> getHistoricalCurve(String deviceId); + + /********************************** + * 用途说明: 根据巡视任务执行情况ID查看详情 + * 参数说明 taskTodoId + * 返回值说明: java.util.Map + ***********************************/ + Map getTaskTodoById(String taskTodoId,String areaId,String bayId,String mainDeviceId); + + /********************************** + * 用途说明: 查询主设备所关联的点位 + * 参数说明 ids + * 参数说明 meterType + * 参数说明 recognitionType + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: java.util.List> + ***********************************/ + List> getDeviceByMainDevice(String mainDeviceId, String meterType, String recognitionType, String startDate, String endDate); + + /********************************** + * 用途说明: 修正巡视结果 + * 参数说明 taskResult + * 返回值说明: boolean + ***********************************/ + boolean updateTaskResultById(TaskResult taskResult); + + /********************************** + * 用途说明: 数据对比分析列表 + * 参数说明 page + * 参数说明 deviceId + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTaskResultList(Page> page, String deviceId,String startDate, String endDate); + + /********************************** + * 用途说明: 数据对比分析曲线 + * 参数说明 deviceId + * 返回值说明: java.util.List> + ***********************************/ + Map getAnalysisCurve(String deviceId, String startDate, String endDate); + + /********************************** + * 用途说明: 导出数据对比分析列表 + * 参数说明 records + * 参数说明 response + * 返回值说明: void + ***********************************/ + void exportTaskResultList(List> records,String exportFields, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 导出异常点位(导出全部) + * 参数说明 page + * 参数说明 taskTodoId + * 参数说明 valid + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getAbnormalDevice(String stationCode, String taskName, + String deviceName, String patroldeviceName, + String componentName, String recognitionType, String status, + String startDate, String endDate); + + /********************************** + * 用途说明: 数据对比分析列表(查询符合条件的所有) + * 参数说明 page + * 参数说明 deviceId + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getTaskResultAll(String deviceId, String startDate, String endDate); + + /********************************** + * 用途说明: 获取当前时间最近的数据行 + * 参数说明 + * 返回值说明: long + ***********************************/ + long getLastTimePosition(String stationCode,String taskName, String startDate, String endDate,String taskState); + + /********************************** + * 用途说明: 更新摄像机工作状态 + * 参数说明 stationcode 变电站编号,patroldeviceCcode 摄像机编号 working 1=巡视中 0=空闲 + * 返回值说明: long + ***********************************/ + boolean updatePatrolDeviceState(String stationcode, String patroldeviceCcode, String working); + + /********************************** + * 用途说明: 根据任务执行情况ID查询点位查询 + * 参数说明 taskTodoId + * 参数说明 deviceName + * 参数说明 valid + * 返回值说明: java.util.List> + ***********************************/ + List> getTaskDeviceById(String taskTodoId, String deviceName, String valid); + + /********************************** + * 用途说明: 查询联动任务列表 + * 参数说明 page + * 参数说明 stationCode + * 参数说明 linkageDeviceName + * 参数说明 taskName + * 参数说明 linkageType + * 参数说明 linkageDeviceType + * 参数说明 startFormat + * 参数说明 endFormat + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getLinkTaskTodoList(Page> page, String stationCode, + String linkageDeviceName, String taskName, String linkageType, + String linkageDeviceType, String startDate, String endDate); + + /********************************** + * 用途说明: 获取点位数量 + * 参数说明 taskTodoId + * 返回值说明: long + ***********************************/ + long getTaskResultCount(String taskTodoId); + + /********************************** + * 用途说明: 获取异常点位 + * 参数说明 taskTodoId + * 返回值说明: 任务结果结果集合 + ***********************************/ + List getAbnormalResult(String taskTodoId); + + /********************************** + * 用途说明: 巡视设备巡检统计 + * 参数说明 page + * 参数说明 patrolDeviceCode + * 参数说明 patrolDeviceName + * 参数说明 type + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getDeviceInspectionStat(Page> page, String patrolDeviceCode, + String patrolDeviceName, String type); + + /********************************** + * 用途说明: 巡视设备巡检统计(上报上级) + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getDeviceInspectionStat(); + + /********************************** + * 用途说明: 巡视任务数据统计 + * 参数说明 page + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTaskDataStat(Page> page, String stationCode, String type, + String startDate, + String endDate); + + /********************************** + * 用途说明: 巡视任务数据统计 + * 参数说明 page + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + List> getTaskDataStat(String stationCode, String type, String startDate, String endDate); + + /********************************** + * 用途说明: 根据任务id查询未审核告警和数量 + * 参数说明 taskTodoIds + * 返回值说明: java.util.List> + ***********************************/ + List> getNotCheckAlarmInfo(String taskTodoIds); + + /********************************** + * 用途说明: 设置实物ID + * 参数说明 resultId + * 参数说明 filePath + * 返回值说明: void + ***********************************/ + void setMaterialIdByImg(String resultId, String filePath); + + /********************************** + * 用途说明: 调用温度的sdk识别并给图片加上温度 + * 参数说明 resultId + * 参数说明 objectId + * 参数说明 patrolDeviceCode + * 参数说明 fullfilename + * 返回值说明: com.alibaba.fastjson.JSONObject + ***********************************/ + JSONObject callPicAnalyse(String resultId, String objectId, String patrolDeviceCode, String rtspurl, String fullfilename, + String region, String csvFilePath) throws IOException; + + /********************************** + * 用途说明: 调用声音检测程序 + * 参数说明 resultId + * 参数说明 objectId + * 参数说明 fullfilename + * 返回值说明: com.alibaba.fastjson.JSONObject + ***********************************/ + JSONObject callSoundAnalyse(String resultId, String objectId) throws IOException; + + /********************************** + * 用途说明: 设置请求分析主机调用失败的任务为巡视失败 + * 参数说明 stationCode + * 参数说明 resultId + * 参数说明 deviceSumnum + * 参数说明 taskTodoId + * 返回值说明: void + ***********************************/ + void setTaskResultAndTodo(String stationCode, String resultId, Integer deviceSumnum, String taskTodoId); + + /********************************** + * 用途说明: 查询联动任务最近十条 + * 参数说明 linkageType 联动类型 + * 返回值说明: java.util.List> + ***********************************/ + List> getLinkTaskRecordLast(String stationCode, String linkageType,String taskName); + + /********************************** + * 用途说明: 查询联动控制巡视任务 + * 参数说明 stationCode + * 参数说明 todoId + * 参数说明 linkageType + * 返回值说明: java.util.List> + ***********************************/ + List> getLinkTaskTodo(String stationCode, String todoId, String linkageType); + + /********************************** + * 用途说明: 按照日、周、月统计巡视任务 + * 参数说明 type + * 返回值说明: java.util.List> + ***********************************/ + List> getTaskDurationStat(String startDate, String endDate, String type); + + /********************************** + * 用途说明: 按照日、周、月统计巡视任务导出 + * 参数说明 startDate + * 参数说明 endDate + * 参数说明 type + * 参数说明 response + * 返回值说明: void + ***********************************/ + void exportTaskDurationStat(String startDate, String endDate, String type, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 巡视任务数据统计 + * 参数说明 page + * 参数说明 stationCode + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 参数说明 response + * 返回值说明: void + ***********************************/ + void exportTaskDataStat(Page> page, String stationCode, String type, String startDate, + String endDate, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 获取历史点位 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 参数说明 deviceId 点位id + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getHistoryDevice(Page> page, String startDate, String endDate, String deviceId); + + /********************************** + * 用途说明: 查询非同源对比分析列表 + * 参数说明 stationCode 变电站编号 + * 参数说明 taskName 任务名称 + * 参数说明 mainDeviceName 主设备名称 + * 参数说明 componentName 部件名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: java.util.List> + ***********************************/ + List> getNonHomologousAnalysis(String stationCode, String taskName, String mainDeviceName, String componentName, String recognitionType, String startFormat, String endFormat); + + /********************************** + * 用途说明: 查询非同相对比分析列表 + * 参数说明 stationCode 变电站编号 + * 参数说明 taskName 任务名称 + * 参数说明 mainDeviceName 主设备名称 + * 参数说明 componentName 部件名名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: java.util.List> + ***********************************/ + List> getNonCoherentAnalysis(String stationCode, String taskName, String mainDeviceName, String componentName, String recognitionType, String startFormat, String endFormat); + + /********************************** + * 用途说明: 查询趋势变化点位列表 + * 参数说明 page 分页参数 + * 参数说明 stationCode 变电站名称 + * 参数说明 taskName 任务名称 + * 参数说明 mainDeviceName 主设备名称 + * 参数说明 componentName 部件名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 deviceName 点位名称 + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + Page> getTrendChangeDeviceList(Page> page, String stationCode, + String mainDeviceName, String componentName, + String recognitionType, String deviceName, String startFormat, + String endFormat); + + /********************************** + * 用途说明: 查询趋势变化分析列表 + * 参数说明 deviceId 点位id + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: java.util.Map + ***********************************/ + Map getTrendChangeAnalysisList(String deviceId, String startFormat, String endFormat); + + void executeTask(List> devicelist,TaskTodo quartzJob,String linkageDeviceType) throws InterruptedException; + + ResponseResult getThreadTask(String taskTodoId); + + void sendTaskResultById(String id); + + boolean onceAnalyse(String id) throws Exception; + + void exportMeterDataByTaskTodoId(String taskTodoId, HttpServletResponse response); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/AlarmLogServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/AlarmLogServiceImpl.java new file mode 100644 index 0000000..c855063 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/AlarmLogServiceImpl.java @@ -0,0 +1,1928 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.*; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.component.mqtt.MqttGateway; +import com.yfd.platform.component.nettyclient.*; +import com.yfd.platform.config.AlarmTemplateConfig; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.LinkageSignalMapper; +import com.yfd.platform.modules.basedata.mapper.SubstationDeviceMapper; +import com.yfd.platform.modules.basedata.mapper.SubstationMapper; +import com.yfd.platform.modules.basedata.mapper.SubstationPatroldeviceMapper; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.AlarmLogMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.IAlarmLogService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.mapper.SysOrganizationMapper; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.SecurityUtils; +import io.netty.util.CharsetUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.*; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + *

+ * 告警日志表 服务实现类 + *

+ * + * @since 2023-05-03 + */ +@Service +@Slf4j +public class AlarmLogServiceImpl extends ServiceImpl implements IAlarmLogService { + + @Resource + private SubstationDeviceMapper deviceMapper; + + @Resource + private SubstationPatroldeviceMapper patroldeviceMapper; + + @Resource + private TaskTodoMapper taskTodoMapper; + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private SubstationMapper stationMapper; + @Resource + private SubstationDeviceMapper substationDeviceMapper; + @Resource + private HttpServerConfig Config; + @Resource + private AlarmLogMapper alarmLogMapper; + @Resource + private ITaskTodoService taskTodoService; + @Resource + private AlarmTemplateConfig alarmTemplateConfig; + @Resource + private SysOrganizationMapper sysOrganizationMapper; + @Resource + IStationRobotService stationNodeService; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private ParentConfig parentConfig; + @Resource + private IMessageService messageService; + + @Resource + private LinkageSignalMapper linkageSignalMapper; + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + @Resource + private MqttGateway mqttGateway; + + @Override + public synchronized boolean updateTaskResult(JSONObject jsonObject) throws Exception { + JSONArray resultList = jsonObject.getJSONArray("resultList"); + for (int i = 0; i < resultList.size(); i++) { + JSONObject resultObject = resultList.getJSONObject(i); + if (resultObject == null) { + continue; + } + String resultId = resultObject.getStr("objectId"); + TaskResult taskResult = taskResultMapper.selectById(resultId); + JSONArray results = resultObject.getJSONArray("results"); + //设置任务结果数据 + updateTaskResult(taskResult, results); + + taskResultMapper.updateById(taskResult); + //更新当前任务状态及进度信息 + TaskTodo taskTodo = taskTodoMapper.selectById(taskResult.getTaskTodoId()); + int sumnum = taskTodo.getDeviceSumnum(); + // 赋值执行数量 + List> list = taskTodoService.getTaskResultById(taskTodo.getTaskTodoId(), null, null, null); + // 已送检点数 + long patrolCount = list.stream().filter(t -> !("0").equals(t.get("flag")) && !("1").equals(t.get("flag"))).count(); + // 失败的数量 + long failCount = + list.stream().filter(t -> ("4").equals(t.get("flag")) || ("6").equals(t.get("flag"))).count(); + // 异常数量 + long abnormalCount = list.stream().filter(t -> ("2").equals(t.get("valid1"))).count(); + taskTodo.setDeviceDeforeNum((int) patrolCount); + taskTodo.setDeviceUnusualnum((int) abnormalCount); + taskTodo.setDeviceFailureNum((int) failCount); + int patrolnum = taskResultMapper.selectCount( + new LambdaQueryWrapper(). + eq(TaskResult::getTaskTodoId, taskResult.getTaskTodoId()). + ne(TaskResult::getFlag, "0") + ); + int percent = 0; + percent = patrolnum * 100 / sumnum; //百分之多少 + if (!"100".equals(taskTodo.getTaskProgress())) { + taskTodo.setTaskProgress(String.valueOf(percent)); + } + boolean b = patrolnum == sumnum; + if (b) { + //全部完成 + taskTodo.setTaskState("1"); //已完成 + taskTodo.setEndTime(DateUtil.now()); + } + taskTodo.setLastmodifydate(new Timestamp(DateUtil.current())); + taskTodoMapper.updateById(taskTodo); + // 向上级发送任务数据 + taskTodoService.sendTaskResult(taskTodo, taskResult); + Map map = taskTodoService.getTaskTodoById(taskResult.getTaskTodoId(), null, null, null); + String stationCode = taskResult.getStationCode(); +// if (StrUtil.isNotBlank(taskTodo.getControlNum())) { +// if (b) { +// WebSocketServer.sendInfo("taskresult_" + stationCode, JSONUtil.toJsonStr(JSONUtil.parse(map))); +// } +// } else { +// WebSocketServer.sendInfo("taskresult_" + stationCode, JSONUtil.toJsonStr(JSONUtil.parse(map))); +// } + WebSocketServer.sendInfo("taskresult_" + stationCode, JSONUtil.toJsonStr(JSONUtil.parse(map))); + if (taskTodo.getTaskType().equals("4")) { + String controlNum = taskTodo.getControlNum(); + List linkageSignals = + linkageSignalMapper.selectList(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, controlNum)); + if (linkageSignals.size() > 0) { + LinkageSignal linkageSignal = linkageSignals.get(0); + String linkageType = linkageSignal.getLinkageType(); + if ("1".equals(linkageType)) { + //对于一键顺控任务调用顺控文件生成和处理方法 + stationNodeService.createVideocfmresult(taskResult.getStationCode(), taskResult.getTaskCode(), + taskResult.getDesc()); + } + } + + } + + } + return true; + } + + @Override + public boolean updateDeviceResult(JSONObject dataObject) throws Exception { + JSONArray resultList = dataObject.getJSONArray("resultList"); + for (int i = 0; i < resultList.size(); i++) { + JSONObject resultObject = resultList.getJSONObject(i); + if (resultObject == null) { + continue; + } + String objectId = resultObject.getStr("objectId"); + SubstationDevice substationDevice = substationDeviceMapper.selectById(objectId); + JSONArray results = resultObject.getJSONArray("results"); + //设置点位结果数据 + + Set validList = new HashSet<>(); + List valueList = new ArrayList<>(); + List confList = new ArrayList<>(); + List descList = new ArrayList<>(); + List typeList = new ArrayList<>(); + String filename = ""; + JSONArray posArray = new JSONArray(); + JSONObject map = new JSONObject(); + String resImagePath = ""; + for (int a = 0; a < results.size(); a++) { + JSONObject result = results.getJSONObject(a); + String code = result.getStr("code"); + if ("2000".equals(code)) { + String type = result.getStr("type"); + typeList.add(type); + String value = result.getStr("value"); + String desc = result.getStr("desc"); + String conf = result.getStr("conf"); + if (ObjectUtil.isNotEmpty(conf)) { + confList.add(conf); + } + String valueType = result.getStr("type"); + validList.add("1"); + String posStr = ""; + if (!"infrared".equals(result.getStr("type")) && !"sound".equals(result.getStr("type")) && !"yjsk" + .equals(result.getStr("type")) && !"isolator".equals(result.getStr("type"))) { + //图片 排除声音 + // filename = StrUtil.subAfter(URLDecoder.decode(result.getStr + // ("resImagePath"), "utf-8"), "=", + // true); + filename = result.getStr("resImagePath"); + String pos = result.getStr("pos"); + // 将框坐标统一存储画框 + if (StrUtil.isNotBlank(pos)) { + JSONArray jsonArray = JSONUtil.parseArray(pos); + // 写入内容到文件 + if (jsonArray.size() > 0) { + JSONObject jsonObject = jsonArray.getJSONObject(0); + if (jsonObject.size() > 0) { + posArray.addAll(jsonArray); + JSONArray areas = jsonObject.getJSONArray("areas"); + JSONObject jsonObject1 = areas.getJSONObject(0); + JSONObject jsonObject2 = areas.getJSONObject(1); + posStr = + " " + jsonObject1.getStr("x") + " " + jsonObject1.getStr("y") + " " + jsonObject2.getStr("x") + " " + jsonObject2.getStr("y"); + } + } + + } + } else { + resImagePath = result.getStr("resImagePath"); + } + Map judgeresult = judgeAbnormal(result.getStr("type"), result.getStr("value"), + result.getStr("conf"), substationDevice.getDeviceId(), ""); + // || "yjsk".equals(result.getStr("type")) + log.info("=======================发送结果=======================" + result.getStr("type")); + if (!("meter".equals(type) || "infrared".equals(type) || "sound".equals(type) || StrUtil.isBlank(type) || "yjsk".equals(result.getStr("type")) || "isolator".equals(result.getStr("type")))) { + // 图像缺陷分析类型 + List> pictureDefectAnalysisTypeList = + sysDictionaryItemsService.getDeviceByType( + "PictureDefectAnalysisType"); + Set customList = + pictureDefectAnalysisTypeList.stream().filter(c -> ObjectUtil.isNotEmpty(c.get( + "custom1"))).map(c -> c.get("custom1").toString()).collect(Collectors.toSet()); + for (String custom : customList) { + if (JSONUtil.isTypeJSONArray(custom)) { + JSONArray jsonArray = JSONUtil.parseArray(custom); + for (int j = 0; j < jsonArray.size(); j++) { + JSONObject jsonObject = jsonArray.getJSONObject(j); + // 检查当前对象的key字段是否与目标key匹配 + if (jsonObject.getStr("key").equals(valueType)) { + valueList.add(jsonObject.getStr("value")); + break; // 找到后跳出循环 + } + } + } + + } + } else { + log.info("==============================进入===========================" + result.getStr("type")); + valueList.add(value); + descList.add(desc); + desc = ""; + } + log.info("=========================================================" + judgeresult.get( + "isNormal").toString()); + //判定为异常 + String resultStr = ""; + if ("0".equals(judgeresult.get("isNormal").toString())) { + //判定为异常 + validList.add("2"); + resultStr = result.getStr("type") + " " + result.getStr("conf") + posStr + "\n"; + } + if (StrUtil.isNotBlank(desc) && ("0".equals(judgeresult.get("isNormal").toString()) || valueType.contains("fhz") || valueType.contains("isolator"))) { + descList.add(desc); + } + } else { + // 非2000的判定为失败 + validList.add("0"); + descList.add("识别失败"); + } + } + log.info("========================validList.toString()=================================" + validList.toString()); + // 识别成功 + String value = StrUtil.join(",", valueList); + String descJoin = StrUtil.join(",", descList); + String confJoin = StrUtil.join(",", confList); + String now = DateUtil.now(); + String typeStr = StrUtil.join(",", typeList); + String fileName = URLEncoder.encode(filename, "utf-8"); + String defectFilePath = fileName; + if (StrUtil.isNotBlank(filename) && posArray.size() > 0) { + String originfilename = Config.getSnapFilePath() + filename; + String markedfilename = Config.getSnapFilePath() + StrUtil.replace(filename, ".jpg", "_识别.jpg"); + FileUtil.MarkImageRectangle(originfilename, posArray.toString(), markedfilename); + //水印字体 + Font font = new Font("微软雅黑", Font.PLAIN, 40); + //水印图片色彩 + Color color1 = new Color(31, 183, 55); + FileUtil.addWaterMark(markedfilename, markedfilename, substationDevice.getDeviceName(), 20, -40, color1, + font); + Color color2 = new Color(226, 7, 24); + String alertinfo = descJoin + " " + "置信度:" + confJoin + " " + now; + FileUtil.addWaterMark(markedfilename, markedfilename, alertinfo, 600, -40, color2, font); + defectFilePath = URLEncoder.encode(StrUtil.replace(filename, ".jpg", "_识别.jpg"), "utf-8");//转码存储 + } + map.putOpt("filePath", fileName); + if (StrUtil.isBlank(defectFilePath)) { + map.putOpt("defectFilePath", resImagePath); + } else { + map.putOpt("defectFilePath", defectFilePath); + } + map.putOpt("value", value); + map.putOpt("desc", descJoin); + map.putOpt("valueType", typeStr); + map.putOpt("conf", confJoin); + map.putOpt("time", now); + map.putOpt("patroldeviceDate", now); + String flag = "2"; + String valid = "1"; + // 判定巡视结果 + if (validList.contains("2")) { + // 只要存在异常就判定为异常 + flag = "2"; + valid = "2"; + } + if (validList.size() == 1 && validList.contains("1")) { + // 都为正常的判定为正常 + flag = "2"; + valid = "1"; + } + if (validList.size() == 1 && validList.contains("0")) { + // 都为巡视失败的判定为失败 + flag = "4"; + valid = "0"; + } + map.putOpt("flag", flag); + map.putOpt("deviceId", substationDevice.getDeviceId()); + map.putOpt("recognitionType", substationDevice.getRecognitionTypeList()); + map.putOpt("valid", valid); + map.putOpt("meterType", substationDevice.getMeterType()); + String analysisResult = ""; + if ("4".equals(flag)) { + analysisResult = "识别失败"; + } else if ("2".equals(valid)) { + analysisResult = "异常"; + } else { + analysisResult = "正常"; + } + map.putOpt("analysisResult", analysisResult); + log.info("--------------------------" + map.toString()); + // 发送数据到前端 + WebSocketServer.sendInfo("deviceresult_" + substationDevice.getStationCode(), map.toString()); + } + return true; + } + + /********************************** + * 用途说明: 设置任务结果数据 + * 参数说明 taskResult + * 参数说明 result + * 返回值说明: com.yfd.platform.modules.patroltask.domain.TaskResult + ***********************************/ + private void updateTaskResult(TaskResult taskResult, JSONArray results) throws Exception { + //正确 2000 图像数据错误 2001 算法分析失败 2002 + + Set validList = new HashSet<>(); + List valueList = new ArrayList<>(); + List confList = new ArrayList<>(); + List descList = new ArrayList<>(); + List typeList = new ArrayList<>(); + String filename = ""; + JSONArray posArray = new JSONArray(); + Map map = new HashMap<>(); +// String filePath1 = URLDecoder.decode(taskResult.getFilePath(), "utf-8"); +// String fileName = StrUtil.subAfter(filePath1, "/", true).replace("_原图.jpg", ".txt").replace("_原图.jpeg", ".txt").replace("_原图.JPG", ".txt").replace("_原图.png", ".txt"); + // 获取todo信息创建文件 +// TaskTodo taskTodo = taskTodoMapper.selectById(taskResult.getTaskTodoId()); +// String planStartTime = taskTodo.getPlanStartTime(); +// DateTime date = DateUtil.parse(planStartTime); +// String dateFormat = DateUtil.format(date, "yyyyMMddHHmmss"); +// String recognition = taskTodo.getTaskName() + dateFormat; +// String filePath = httpServerConfig.getSnapFilePath() + recognition + File.separator + fileName; +// File file = cn.hutool.core.io.FileUtil.touch(filePath); +// FileWriter writer = new FileWriter(file); + for (int a = 0; a < results.size(); a++) { + JSONObject result = results.getJSONObject(a); + String code = result.getStr("code"); + if ("2000".equals(code)) { + String type = result.getStr("type"); + typeList.add(type); + taskResult.setValueType(result.getStr("type")); + String value = result.getStr("value"); + String desc = result.getStr("desc"); + String conf = result.getStr("conf"); + if (ObjectUtil.isNotEmpty(conf)) { + confList.add(conf); + } + validList.add("1"); + String posStr = ""; + if (!"infrared".equals(result.getStr("type")) && !"sound".equals(result.getStr("type")) && !"yjsk".equals(result.getStr("type")) && !"isolator".equals(result.getStr("type"))) { + //图片 排除声音 +// filename = StrUtil.subAfter(URLDecoder.decode(result.getStr("resImagePath"), "utf-8"), "=", +// true); + filename = result.getStr("resImagePath"); + String pos = result.getStr("pos"); + // 将框坐标统一存储画框 + if (StrUtil.isNotBlank(pos)) { + JSONArray jsonArray = JSONUtil.parseArray(pos); + // 写入内容到文件 + if (jsonArray.size() > 0) { + JSONObject jsonObject = jsonArray.getJSONObject(0); + if (jsonObject.size() > 0) { + posArray.addAll(jsonArray); + JSONArray areas = jsonObject.getJSONArray("areas"); + JSONObject jsonObject1 = areas.getJSONObject(0); + JSONObject jsonObject2 = areas.getJSONObject(1); + posStr = " " + jsonObject1.getStr("x") + " " + jsonObject1.getStr("y") + " " + jsonObject2.getStr("x") + " " + jsonObject2.getStr("y"); + } + } + + } + } else { + taskResult.setDefectFilePath(taskResult.getFilePath()); + } + String valueType = taskResult.getValueType(); + Map judgeresult = judgeAbnormal(result.getStr("type"), result.getStr("value"), + result.getStr("conf"), taskResult.getDeviceId(), taskResult.getResultId()); + // || "yjsk".equals(result.getStr("type")) + log.info("=======================发送结果=======================" + result.getStr("type")); + if (!("meter".equals(type) || "infrared".equals(type) || "sound".equals(type) || StrUtil.isBlank(type) || "yjsk".equals(result.getStr("type")) || "isolator".equals(result.getStr("type")))) { + // 图像缺陷分析类型 + List> pictureDefectAnalysisTypeList = sysDictionaryItemsService.getDeviceByType( + "PictureDefectAnalysisType"); + Set customList = + pictureDefectAnalysisTypeList.stream().filter(c -> ObjectUtil.isNotEmpty(c.get( + "custom1"))).map(c -> c.get("custom1").toString()).collect(Collectors.toSet()); + for (String custom : customList) { + if (JSONUtil.isTypeJSONArray(custom)) { + JSONArray jsonArray = JSONUtil.parseArray(custom); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + // 检查当前对象的key字段是否与目标key匹配 + if (jsonObject.getStr("key").equals(valueType)) { + valueList.add(jsonObject.getStr("value")); + break; // 找到后跳出循环 + } + } + } + + } + } else { + log.info("==============================进入===========================" + result.getStr( + "type")); + valueList.add(value); + descList.add(desc); + desc = ""; + } + log.info("=========================================================" + judgeresult.get( + "isNormal").toString()); + //判定为异常 +// String resultStr = ""; + if ("0".equals(judgeresult.get("isNormal").toString())) { + map.put("alarmlevel", judgeresult.get("alarmlevel")); + //判定为异常 + validList.add("2"); +// resultStr = result.getStr("type") + " " + result.getStr("conf") + posStr + "\n"; + } + +// writer.write(resultStr); + if (StrUtil.isNotBlank(desc) && ("0".equals(judgeresult.get("isNormal").toString()) || valueType.contains("fhz") || valueType.contains("isolator"))) { + descList.add(desc); + } + } else { + // 非2000的判定为失败 + validList.add("0"); + descList.add("识别失败"); + } + } + log.info("========================validList.toString()=================================" + validList.toString()); +// writer.close(); + // 识别成功 + taskResult.setValue(StrUtil.join(",", valueList)); + String descJoin = StrUtil.join(",", descList); + String confJoin = StrUtil.join(",", confList); + String now = DateUtil.now(); + if (StrUtil.isNotBlank(filename) && StrUtil.isNotBlank(taskResult.getFilePath()) && posArray.size() > 0) { + String originfilename = Config.getSnapFilePath() + filename; + String markedfilename = Config.getSnapFilePath() + StrUtil.replace(filename, ".jpg", "_识别.jpg"); + FileUtil.MarkImageRectangle(originfilename, posArray.toString(), markedfilename); + //水印字体 + Font font = new Font("微软雅黑", Font.PLAIN, 40); + //水印图片色彩 + Color color1 = new Color(31, 183, 55); + FileUtil.addWaterMark(markedfilename, markedfilename, taskResult.getDeviceName(), 20, -40, color1, font); + Color color2 = new Color(226, 7, 24); + String alertinfo = descJoin + " " + "置信度:" + confJoin + " " + now; + FileUtil.addWaterMark(markedfilename, markedfilename, alertinfo, 600, -40, color2, font); + String defectFilePath = + URLEncoder.encode(StrUtil.replace(filename, ".jpg", "_识别.jpg"), "utf-8");//转码存储 + taskResult.setDefectFilePath(defectFilePath); + taskResult.setRectangle(posArray.toString()); + } + //识别结果描述 示例:表计表盘破损 + taskResult.setDesc(descJoin); + //识别结果置信度 示例:0.99 + taskResult.setConf(confJoin); + taskResult.setTime(now); + taskResult.setLastmodifydate(new Timestamp(DateUtil.current())); + taskResult.setValueType(StrUtil.join(",", typeList)); + // 判定巡视结果 + if (validList.contains("2")) { + // 只要存在异常就判定为异常 + taskResult.setFlag("2"); + taskResult.setValid("2"); + // 根据巡视点位判断是否生成报警 + if ("1".equals(taskResult.getIsAlarm())) { + log.info("=========================生成报警记录===================="); + //生成报警记录 + createTaskAlarmLog(taskResult, map); + } + } + if (validList.size() == 1 && validList.contains("1")) { + // 都为正常的判定为正常 + taskResult.setFlag("2"); + taskResult.setValid("1"); + } + if (validList.size() == 1 && validList.contains("0")) { + // 都为巡视失败的判定为失败 + taskResult.setFlag("4"); + taskResult.setValid("0"); + } + //TODO 新增加三相对比 +// sendNonCoherentMsg1(taskResult, 0); + } + + /********************************** + * 用途说明: 发送非同相告警 + * 参数说明 taskResult + * 返回值说明: void + ***********************************/ + @Override + public void sendNonCoherentMsg1(TaskResult taskResult, double alertValue) throws Exception { + log.info("=========================三相对比==============================" + taskResult.toString()); + SubstationDevice substationDevice1 = deviceMapper.selectById(taskResult.getDeviceId()); + if ("4".equals(substationDevice1.getRecognitionTypeList())) { + substationDevice1.setMeterType("4"); + } + String meterType = substationDevice1.getMeterType(); + if (!("4".equals(taskResult.getRecognitionType()) || "1".equals(taskResult.getRecognitionType()))) { + return; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(TaskResult::getResultId, taskResult.getResultId()).eq(TaskResult::getTaskTodoId, + taskResult.getTaskTodoId()).eq(TaskResult::getAreaId, + taskResult.getAreaId()).eq(TaskResult::getBayId, taskResult.getBayId()).eq(TaskResult::getMainDeviceId, taskResult.getMainDeviceId()).eq(TaskResult::getComponentId, taskResult.getComponentId()).eq(TaskResult::getRecognitionType, taskResult.getRecognitionType()).eq(TaskResult::getFlag, "2"); + List> mapList = taskResultMapper.selectMaps(queryWrapper); + log.info("=========================三相对比数量==============================" + mapList.size()); + Map map1 = BeanUtil.beanToMap(taskResult); + mapList.add(map1); + if (mapList.size() < 3) { + return; + } + log.info("=========================三相对比数量==============================" + mapList.size()); + mapList.forEach(r -> { + String deviceId = r.get("deviceId").toString(); + SubstationDevice substationDevice = deviceMapper.selectById(deviceId); + String meterType1 = substationDevice.getMeterType(); + if ("4".equals(substationDevice.getRecognitionTypeList())) { + meterType1 = "4"; + } + r.put("meterType", meterType1); + r.put("phase", substationDevice.getPhase()); + // if (taskResult.getResultId().equals(r.get("resultId").toString())) { + // r.put("value", taskResult.getValue()); + // } + }); + log.info("=========================三相对比数据==============================" + mapList.toString()); + Map>> meterTypeMap = + mapList.stream().collect(Collectors.groupingBy(m -> m.get("meterType").toString())); + List> mapList1 = meterTypeMap.get(meterType); + int phaseSize = mapList1.stream().collect(Collectors.groupingBy(m -> m.get("phase").toString())).size(); + if (!(mapList1.size() == 3 && phaseSize == 3)) { + return; + } + Map map = new HashMap<>(); + // 三相对比 + if ("1".equals(taskResult.getRecognitionType())) { + map.put("alarmtype", "9"); + map.put("alarmlevel", "4"); + alertValue = 1; + // 仪表三相 + List values = + mapList1.stream().map(m -> NumberUtil.parseDouble(m.get("value").toString())).collect(Collectors.toList()); + List deviceNames = + mapList1.stream().map(m -> NumberUtil.parseDouble(m.get("deviceName").toString())).collect(Collectors.toList()); + Double max = CollectionUtil.max(values); + Double min = CollectionUtil.min(values); + String valueStr = StrUtil.join(",", values); + String deviceNameStr = StrUtil.join(",", deviceNames); + double difference = max - min; + if (difference > alertValue) { + String alarmInfo = String.format("仪表三相告警提示,点位【%s】,值【%s】,超过阈值%s告警!", deviceNameStr, valueStr, + alertValue); + map.put("alarmInfo", alarmInfo); + map.put("alertValue", alertValue + ""); + // 创建仪表三相告警 + createOtherAlarm(taskResult, map); + } + } + + if ("4".equals(taskResult.getRecognitionType())) { + map.put("alarmtype", "3"); + map.put("alarmlevel", "4"); + alertValue = 10; + // 仪表三相 + List values = + mapList1.stream().map(m -> NumberUtil.parseDouble(m.get("value").toString())).collect(Collectors.toList()); + List deviceNames = + mapList1.stream().map(m -> m.get("deviceName").toString()).collect(Collectors.toList()); + Double max = CollectionUtil.max(values); + Double min = CollectionUtil.min(values); + String valueStr = StrUtil.join(",", values); + String deviceNameStr = StrUtil.join(",", deviceNames); + double difference = max - min; + if (difference > alertValue) { + String alarmInfo = String.format("三相温差报警告警提示,点位【%s】,值【%s】,超过阈值%s告警!", deviceNameStr, valueStr, + alertValue); + map.put("alarmInfo", alarmInfo); + map.put("alertValue", alertValue + ""); + // 创建三相温差告警 + createOtherAlarm(taskResult, map); + } + } + } + + private boolean createOtherAlarm(TaskResult taskResult, Map map) throws UnsupportedEncodingException { + AlarmLog alarmLog = new AlarmLog(); + alarmLog.setId(IdUtil.fastSimpleUUID()); + String deviceid = taskResult.getDeviceId(); + SubstationDevice device = deviceMapper.selectById(deviceid); + String alarmDate = DateUtil.now(); + Substation substation = stationMapper.selectOne(new LambdaQueryWrapper(). + eq(Substation::getStationCode, device.getStationCode())); + alarmLog.setStationCode(device.getStationCode()); + alarmLog.setStationId(substation.getStationId()); + alarmLog.setStationName(substation.getStationName()); + alarmLog.setAreaId(taskResult.getAreaId()); + alarmLog.setAreaName(taskResult.getAreaName()); + alarmLog.setBayId(taskResult.getBayId()); + alarmLog.setBayName(taskResult.getBayName()); + alarmLog.setMainDeviceId(taskResult.getMainDeviceId()); + alarmLog.setMainDeviceName(taskResult.getMainDeviceName()); + alarmLog.setComponentId(taskResult.getComponentId()); + alarmLog.setComponentName(taskResult.getComponentName()); + alarmLog.setDeviceId(taskResult.getDeviceId()); + alarmLog.setDeviceName(taskResult.getDeviceName()); + alarmLog.setPatroldeviceCode(taskResult.getPatroldeviceCode()); + alarmLog.setPatroldeviceName(taskResult.getPatroldeviceName()); + alarmLog.setMaterialId(taskResult.getMaterialId()); + TaskTodo taskTodo = taskTodoMapper.selectById(taskResult.getTaskTodoId()); + alarmLog.setTaskId(taskTodo.getTaskId()); + alarmLog.setTaskPatrolledId(taskResult.getTaskTodoId()); + alarmLog.setTaskResultId(taskResult.getResultId()); + alarmLog.setTaskCode(taskResult.getTaskCode()); + alarmLog.setTaskName(taskResult.getTaskName()); + alarmLog.setTaskAlarmType("1"); //'1:巡视任务告警;2:静默任务告警,3:巡视设备告警', + //alarmLog.setMonitorType("");//非静默监视不填 + alarmLog.setAlarmType(ObjUtil.isNotEmpty(map.get("alarmtype")) ? map.get("alarmtype") : ""); + //'字典表<1>: = 超温报警<2>: = 温升报警<3>: = 三相温差报警<4>: = 三相对比报警<5>: = 声音异常<6>: = 外观异常<7>: = 仪表越限报警<8>: = 仪表超量程报警<9>: = + // 仪表三相对比<10>: = 变位报警', + alarmLog.setAlarmLevel(ObjUtil.isNotEmpty(map.get("alarmlevel")) ? map.get("alarmlevel") : "4");// + // '1:预警;2:一般;3:严重;4;危急', 待判断细分 + alarmLog.setRecognitionType(taskResult.getRecognitionType());//'1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6 + // :闪烁检测11:局放超声波检测12:局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测', + alarmLog.setFileType(taskResult.getFileType());//'1:红外图谱;2:可见光照片;3:音频4:视频5:识别图片,存在多个文件,文件类型用逗号分隔', + //告警文件目录规范:地市名/区域名/变电站名/分类/年月 + String type = "缺陷";//缺陷、判别 + String alarmfilepath = String.format("%s/%s/%s/%s/%s/", + substation.getCityName(), + substation.getSectionName(), + substation.getStationName(), + type, + DateUtil.format(DateUtil.date(), "yyyyMM") + ); + log.info( + "============================================拷贝原图到报警目录下============================================"); + //拷贝原图到报警目录下 + String fullfilename = URLDecoder.decode(taskResult.getFilePath(), "utf-8"); + String alarmfullfilename = alarmfilepath + StrUtil.subAfter(fullfilename, "/", true); + FileUtil.copy(Config.getSnapFilePath() + fullfilename, Config.getAlarmFilePath() + alarmfullfilename, true); + alarmLog.setFilePath(URLEncoder.encode(alarmfullfilename, "utf-8"));//原图文件名称(含路径) + alarmLog.setDefectType(taskResult.getValueType());//'缺陷类别' + log.info( + "============================================拷贝识别图到报警目录下============================================"); + //拷贝识别图到报警目录下 + if (ObjUtil.isNotEmpty(taskResult.getDefectFilePath())) { + String fulldefectfilename = URLDecoder.decode(taskResult.getDefectFilePath(), "utf-8"); + String alarmfilename = StrUtil.replace(StrUtil.subAfter(fulldefectfilename, "/", true), "识别", "缺陷告警"); + String fullalarmfile = alarmfilepath + alarmfilename; + FileUtil.copy(Config.getSnapFilePath() + fulldefectfilename, Config.getAlarmFilePath() + fullalarmfile, + true); + alarmLog.setDefectFilePath(URLEncoder.encode(fullalarmfile, "utf-8"));//标注了识别框和信息 + } + log.info( + "============================================图形识别结果(值)============================================"); + alarmLog.setValue(taskResult.getValue());//图形识别结果(值) + alarmLog.setUnit(taskResult.getUnit()); + String alarminfo = map.get("alarmInfo"); + alarmLog.setContent(alarminfo);//告警描述信息 + alarmLog.setAlarmDate(alarmDate);//发送报警时间 + alarmLog.setCheckFlag("0");//'0:未核查;1:已审核;2.已修正' + alarmLog.setRectangle(taskResult.getRectangle());//'格式:x1,y1,x2y2;x3,y3,x4,y4;多个报警框的左上角和右下角坐标' + alarmLog.setDatastatus("1"); //初始创建 + alarmLog.setLastmodifier("系统创建"); + alarmLog.setLastmodifydate(new Timestamp(DateUtil.current())); +// this.save(alarmLog);//保存此报警数据 + Map logMap = BeanUtil.beanToMap(alarmLog); + String stationId = alarmLog.getStationId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getStationId, stationId).eq(AlarmLog::getCheckFlag + , 0); + int count = this.count(queryWrapper); + logMap.put("alarmCount", count); + JSONObject jsonObject = new JSONObject(logMap); + String stationCode = substation.getStationCode(); +// SysOrganization sysOrganization = +// sysOrganizationMapper.selectOne(new LambdaUpdateWrapper().eq(SysOrganization::getOrgcode, stationCode)); +// String parentid = sysOrganization.getParentid(); + jsonObject.putOpt("deviceid", jsonObject.getStr("deviceId")); + jsonObject.putOpt("channelId", taskResult.getPatroldeviceChannelcode()); + jsonObject.putOpt("deviceId", taskResult.getPatroldeviceCode()); + // 告警阈值 + jsonObject.putOpt("earlyMin", map.get("alertValue")); + jsonObject.putOpt("earlyMax", map.get("alertValue")); + jsonObject.putOpt("sameMin", map.get("alertValue")); + jsonObject.putOpt("sameMax", map.get("alertValue")); + jsonObject.putOpt("seriousMin", map.get("alertValue")); + jsonObject.putOpt("seriousMax", map.get("alertValue")); + jsonObject.putOpt("criticalMin", map.get("alertValue")); + jsonObject.putOpt("criticalMax", map.get("alertValue")); + // 如果是站端则区域也需要发送告警 +// if (!"0".equals(parentid)) { +// WebSocketServer.sendInfo(parentid, jsonObject.toString()); +// } + WebSocketServer.sendInfo(stationId, jsonObject.toString()); + return true; + } + + /********************************** + * 用途说明: 发送非同相告警 + * 参数说明 taskResult + * 返回值说明: void + ***********************************/ + private void sendNonCoherentMsg(TaskResult taskResult, double alertValue, String meterType) { + String recognitionTypeList = taskResult.getRecognitionType(); + List> nonCoherentAnalysis = + taskResultMapper.getNonCoherentAnalysis(taskResult.getTaskTodoId(), null, null, null, null, null, + null, null); + List> mapList = + nonCoherentAnalysis.stream().filter(m -> m.get("recognition_type").toString().equals(recognitionTypeList) && m.get("meterType").toString().equals(meterType)).collect(Collectors.toList()); + Set phases = mapList.stream().map(m -> m.get("phase").toString()).collect(Collectors.toSet()); + Set deviceIds = mapList.stream().map(m -> m.get("deviceId").toString()).collect(Collectors.toSet()); + // 如果包含相同相位或者相同点位则跳出 + if (phases.size() != mapList.size() || deviceIds.size() != mapList.size()) { + return; + } + for (Map map : mapList) { + if (ObjectUtil.isNotEmpty(map.get("value")) && StrUtil.isNotBlank(taskResult.getValue())) { + BigDecimal value1 = new BigDecimal(taskResult.getValue()); + BigDecimal value2 = new BigDecimal(map.get("value").toString()); + double difference = Math.abs(value1.subtract(value2).doubleValue()); + if (difference <= alertValue) { + break; + } + Message message = new Message(); + TaskTodo taskTodo = taskTodoService.getById(taskResult.getTaskTodoId()); + String taskName = taskResult.getTaskName(); + // 消息标题 + String title = taskName + "(" + taskTodo.getStartTime() + ")"; + message.setTitle(title); + message.setContent("表记巡视非同相结果差值超阈,结果一:" + map.get("value").toString() + "结果二:" + taskResult.getValue() + + "单位:" + taskResult.getUnit() + "阈值:" + alertValue); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + // 告警触发 + message.setType("4"); + message.setStatus("1"); + // 发送告警 + messageService.save(message); + return; + + } + } + } + + /********************************** + * 用途说明: 发送非同源告警 + * 参数说明 taskResult + * 返回值说明: void + ***********************************/ + private void sendNonHomologousMsg(TaskResult taskResult, double alertValue) { + String deviceId = taskResult.getDeviceId(); + String taskTodoId = taskResult.getTaskTodoId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskResult::getTaskTodoId, taskTodoId).eq(TaskResult::getDeviceId, deviceId).ne(TaskResult::getResultId, taskResult.getResultId()); + List taskResultList = taskResultMapper.selectList(queryWrapper); + String content = ""; + for (TaskResult taskresult : taskResultList) { + // 表记 + if ("1".equals(taskResult.getRecognitionType()) && ObjectUtil.isNotEmpty(taskResult.getValue()) && ObjectUtil.isNotEmpty(taskresult.getValue())) { + BigDecimal value1 = new BigDecimal(taskResult.getValue()); + BigDecimal value2 = new BigDecimal(taskresult.getValue()); + double difference = Math.abs(value1.subtract(value2).doubleValue()); + if (difference <= alertValue) { + break; + } + content = + "表记巡视非同源结果差值超阈,结果一:" + taskresult.getValue() + "结果二:" + taskResult.getValue() + "单位:" + taskResult.getUnit() + "阈值:" + alertValue; + } + // 测温 + if ("4".equals(taskResult.getRecognitionType()) && ObjectUtil.isNotEmpty(taskResult.getValue()) && ObjectUtil.isNotEmpty(taskresult.getValue())) { + String value = taskResult.getValue(); + String value1 = taskresult.getValue(); + String[] split = value.split(","); + String[] split1 = value1.split(","); + if (split.length != 2 || split1.length != 2) { + break; + } +// BigDecimal v1 = new BigDecimal(split[0]); + BigDecimal v2 = new BigDecimal(split[0]); +// BigDecimal v3 = new BigDecimal(split1[0]); + BigDecimal v4 = new BigDecimal(split1[0]); + if (Math.abs(v2.subtract(v4).doubleValue()) <= alertValue) { + break; + } + content = + "红外测温非同源最大温度结果差值超阈,结果一:" + taskresult.getValue() + "结果二:" + taskResult.getValue() + "单位:" + taskResult.getUnit() + "阈值:" + alertValue; + } + + if ("2".equals(taskResult.getRecognitionType())) { + // 设备位置状态识别 + String desc1 = taskResult.getDesc(); + String desc2 = taskresult.getDesc(); + if (StrUtil.isNotBlank(desc1) && StrUtil.isNotBlank(desc2) && !desc1.equals(desc2)) { + // 报警 + content = "设备位置状态识别非同源结果不一致,结果一:" + taskresult.getValue() + "结果二:" + taskResult.getValue() + + "单位:" + taskResult.getUnit(); + } + } + // 如果符合报警条件则报警 + if (StrUtil.isNotBlank(content)) { + Message message = new Message(); + TaskTodo taskTodo = taskTodoService.getById(taskResult.getTaskTodoId()); + String taskName = taskResult.getTaskName(); + // 消息标题 + String title = taskName + "(" + taskTodo.getStartTime() + ")"; + message.setTitle(title); + message.setContent(content); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + // 告警触发 + message.setType("4"); + message.setStatus("1"); + // 发送告警 + messageService.save(message); + } + break; + } + } + + /********************************** + * 用途说明: 告警 + * 参数说明 taskResult 当前结果 + * 返回值说明: void + ***********************************/ + private void sendMessage(TaskResult taskResult) { + String deviceId = taskResult.getDeviceId(); + String taskTodoId = taskResult.getTaskTodoId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskResult::getTaskTodoId, taskTodoId).eq(TaskResult::getDeviceId, deviceId).ne(TaskResult::getResultId, taskResult.getResultId()); + List taskResultList = taskResultMapper.selectList(queryWrapper); + SubstationDevice substationDevice = deviceMapper.selectById(taskResult.getDeviceId()); + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + if (jsonArray.size() != 2) { + return; + } + String alertvalue = "0.0"; + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); //取第二条 + if (ObjectUtil.isEmpty(jsonObject.get("alertvalue"))) { + continue; + } else { + alertvalue = jsonObject.get("alertvalue").toString(); + } + } + + double alertValue = NumberUtil.parseDouble(alertvalue); + String content = ""; + for (TaskResult taskresult : taskResultList) { + if ("1".equals(taskResult.getRecognitionType()) || "4".equals(taskResult.getRecognitionType())) { + // 表记和测温 + if (ObjectUtil.isNotEmpty(taskResult.getValue()) && ObjectUtil.isNotEmpty(taskresult.getValue())) { + BigDecimal value1 = new BigDecimal(taskResult.getValue()); + BigDecimal value2 = new BigDecimal(taskresult.getValue()); + double difference = Math.abs(value1.subtract(value2).doubleValue()); + if (difference <= alertValue) { + break; + } + if ("1".equals(taskResult.getRecognitionType())) { + content = + "表记巡视非同源结果差值超阈,结果一:" + taskresult.getValue() + "结果二:" + taskResult.getValue() + + "单位:" + taskResult.getUnit() + "阈值:" + alertvalue; + } else { + content = + "红外测温非同源结果差值超阈,结果一:" + taskresult.getValue() + "结果二:" + taskResult.getValue() + + "单位:" + taskResult.getUnit() + "阈值:" + alertvalue; + } + + } + + } + if ("2".equals(taskResult.getRecognitionType())) { + // 设备位置状态识别 + String desc1 = taskResult.getDesc(); + String desc2 = taskresult.getDesc(); + if (StrUtil.isNotBlank(desc1) && StrUtil.isNotBlank(desc2) && !desc1.equals(desc2)) { + // 报警 + content = "设备位置状态识别非同源结果不一致,结果一:" + taskresult.getValue() + "结果二:" + taskResult.getValue() + + "单位:" + taskResult.getUnit(); + } + } + // 如果符合报警条件则报警 + if (StrUtil.isNotBlank(content)) { + Message message = new Message(); + TaskTodo taskTodo = taskTodoService.getById(taskResult.getTaskTodoId()); + String taskName = taskResult.getTaskName(); + // 消息标题 + String title = taskName + "(" + taskTodo.getStartTime() + ")"; + message.setTitle(title); + message.setContent(content); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + // 告警触发 + message.setType("4"); + message.setStatus("1"); + // 发送告警 + messageService.save(message); + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + ServerSendEventServer.sendMessage(count + ""); + } + break; + } + if (!"1".equals(taskResult.getRecognitionType()) || StrUtil.isNotBlank(content)) { + return; + } + + // 相位 + String recognitionTypeList = substationDevice.getRecognitionTypeList(); + String meterType = substationDevice.getMeterType(); + List> nonCoherentAnalysis = + taskResultMapper.getNonCoherentAnalysis(taskResult.getTaskTodoId(), null, null, null, null, null, + null, null); + List> mapList = + nonCoherentAnalysis.stream().filter(m -> m.get("recognition_type").toString().equals(recognitionTypeList) && m.get("meterType").toString().equals(meterType)).collect(Collectors.toList()); + Set phases = mapList.stream().map(m -> m.get("phase").toString()).collect(Collectors.toSet()); + Set deviceIds = mapList.stream().map(m -> m.get("deviceId").toString()).collect(Collectors.toSet()); + // 如果包含相同相位或者相同点位则跳出 + if (phases.size() != mapList.size() || deviceIds.size() != mapList.size()) { + return; + } + for (Map map : mapList) { + if (ObjectUtil.isNotEmpty(map.get("value")) && StrUtil.isNotBlank(taskResult.getValue())) { + BigDecimal value1 = new BigDecimal(taskResult.getValue()); + BigDecimal value2 = new BigDecimal(map.get("value").toString()); + double difference = Math.abs(value1.subtract(value2).doubleValue()); + if (difference <= alertValue) { + break; + } + Message message = new Message(); + TaskTodo taskTodo = taskTodoService.getById(taskResult.getTaskTodoId()); + String taskName = taskResult.getTaskName(); + // 消息标题 + String title = taskName + "(" + taskTodo.getStartTime() + ")"; + message.setTitle(title); + message.setContent("表记巡视非同相结果差值超阈,结果一:" + map.get("value").toString() + "结果二:" + taskResult.getValue() + + "单位:" + taskResult.getUnit() + "阈值:" + alertvalue); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + // 告警触发 + message.setType("4"); + message.setStatus("1"); + // 发送告警 + messageService.save(message); + return; + + } + } + + } + + /********************************** + * 用途说明: 静默监视查询 + * 参数说明 stationId + * 参数说明 areaId + * 参数说明 patrolDeviceName + * 参数说明 monitorType + * 返回值说明: java.util.List> + ***********************************/ + @Override + public Page> getAlarmListByType(Page> page, String stationId, + String areaId, String patrolDeviceName, String monitorType) { + return alarmLogMapper.getAlarmListByType(page, stationId, areaId, patrolDeviceName, monitorType); + } + + /********************************** + * 用途说明: 根据设备编号查询告警列表 + * 参数说明 page + * 参数说明 patrolDeviceCode + * 参数说明 monitorType + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getAlarmListPage(Page> page, String patrolDeviceCode, + String monitorType, String startDate, String endDate) { + return alarmLogMapper.getAlarmListPage(page, patrolDeviceCode, monitorType, startDate, endDate); + } + + @Override + public Page> getAlarmLogPage(Page> page, String stationCode, + String taskAlarmType, String alarmLevel, String checkFlag, + String startDate, String endDate) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getStationCode, stationCode); + if (StrUtil.isNotBlank(taskAlarmType)) { + queryWrapper.eq(AlarmLog::getTaskAlarmType, taskAlarmType); + } + if (StrUtil.isNotBlank(alarmLevel)) { + queryWrapper.eq(AlarmLog::getAlarmLevel, alarmLevel); + } + if (StrUtil.isNotBlank(checkFlag)) { + queryWrapper.eq(AlarmLog::getCheckFlag, checkFlag); + } + if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) { + queryWrapper.le(AlarmLog::getAlarmDate, endDate).ge(AlarmLog::getAlarmDate, startDate); + } + queryWrapper.orderByDesc(AlarmLog::getAlarmDate); + return this.pageMaps(page, queryWrapper); + } + + @Override + public boolean setAlarmLogStatus(AlarmLog alarmLog) { + String currentUsername = SecurityUtils.getCurrentUsername(); + //String currentUsername = "admin"; + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp); + alarmLog.setLastmodifier(currentUsername); + alarmLog.setCheckUserName(currentUsername); + alarmLog.setLastmodifydate(timestamp); + alarmLog.setCheckDate(format); + String custom1 = alarmLog.getCustom1(); + if (StrUtil.isNotBlank(custom1) && !"0".equals(custom1)) { + int i = Integer.parseInt(custom1); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime localDateTime = now.plusMinutes(i); + if ("1".equals(alarmLog.getTaskAlarmType())) { + String deviceId = alarmLog.getDeviceId(); + deviceMapper.update(null, new LambdaUpdateWrapper().eq(SubstationDevice::getDeviceId, + deviceId).set(SubstationDevice::getDelayedAlarmDate, localDateTime)); + } else if ("2".equals(alarmLog.getTaskAlarmType())) { + AlarmLog alarmLog1 = this.getById(alarmLog.getId()); + SubstationPatroldevice substationPatroldevice = + patroldeviceMapper.selectOne(new LambdaUpdateWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, alarmLog1.getPatroldeviceCode())); + String monitorType = this.getSlienceType(alarmLog1.getMonitorType()); + String slienceParams = substationPatroldevice.getSlienceParams(); + JSONArray jsonArray = JSONUtil.parseArray(slienceParams); + JSONArray slienceArray = new JSONArray(); + // 维护延时报警截止时间 + for (Object o : jsonArray) { + JSONObject jsonObject = JSONUtil.parseObj(o); + if (monitorType.equals(jsonObject.getStr("type"))) { + jsonObject.putOpt("delayed_alarm_date", LocalDateTimeUtil.formatNormal(localDateTime)); + } + slienceArray.add(jsonObject); + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SubstationPatroldevice::getPatroldeviceId, + substationPatroldevice.getPatroldeviceId()).set(SubstationPatroldevice::getSlienceParams, + slienceArray.toString()); + patroldeviceMapper.update(null, updateWrapper); + } + } + boolean ok = this.updateById(alarmLog); + String id = alarmLog.getId(); + AlarmLog alarmLog1 = this.getById(id); + Map logMap = BeanUtil.beanToMap(alarmLog1); + String stationId = alarmLog.getStationId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getStationId, stationId).eq(AlarmLog::getCheckFlag + , 0); + int count = this.count(queryWrapper); + logMap.put("alarmCount", count); + JSONObject jsonObject = new JSONObject(logMap); + SysOrganization sysOrganization = + sysOrganizationMapper.selectOne(new LambdaUpdateWrapper().eq(SysOrganization::getOrgcode, alarmLog1.getStationCode())); + String parentid = sysOrganization.getParentid(); + if (!"0".equals(parentid)) { + WebSocketServer.sendInfo("check_" + parentid, jsonObject.toString()); + } + WebSocketServer.sendInfo("check_" + alarmLog1.getStationId(), jsonObject.toString()); + return ok; + } + + @Override + public Map getAlarmLogById(String id) { + Map map = this.getMap(new LambdaQueryWrapper().eq(AlarmLog::getId, id)); + if (map == null) { + return ResponseResult.successData(null); + } + String taskResultId = map.get("taskResultId").toString(); + TaskResult taskResult = taskResultMapper.selectById(taskResultId); + if (taskResult != null) { + map.put("deviceId", taskResult.getPatroldeviceCode()); + map.put("channelId", taskResult.getPatroldeviceChannelcode()); + map.put("patroldevicePos", taskResult.getPatroldevicePos()); + } + return map; + } + + private String getSlienceType(String type) { + String sliencetype = ""; + if ("wcgz".equals(type) || "wcaqm".equals(type) || "ryjjph".equals(type) || "xy".equals(type) || + "person_enter".equals(type)) { + sliencetype = "ryxw"; + } else { + sliencetype = type; + } + return sliencetype; + } + + //根据识别类型和结果值判断是否为异常 + private Map judgeAbnormal(String type, String value, String conf, String deviceid, + String resultId) throws Exception { + Map result = new HashMap<>(); + SubstationDevice substationDevice = deviceMapper.selectById(deviceid); + String pictureAnalysisTypeList = substationDevice.getPictureAnalysisTypeList(); + JSONObject jsonObject = JSONUtil.parseObj(pictureAnalysisTypeList); + String pictureDefectAnalysisType = jsonObject.getStr("PictureDefectAnalysisType"); + result.put("isNormal", "1"); + if (StrUtil.isNotBlank(pictureDefectAnalysisType)) { + // 缺陷告警设置 + // List> mapList = sysDictionaryItemsService.getDeviceByType + // ("PictureDefectAnalysisType"); + if ("1".equals(value)) { + result.put("isNormal", "0"); + } + return result; + } + String PictureDiscriminateAnalysisType = jsonObject.getStr("PictureDiscriminateAnalysisType"); + if (StrUtil.isNotBlank(PictureDiscriminateAnalysisType)) { + if ("1".equals(value)) { + result.put("isNormal", "0"); + } + return result; + } + if ("meter".equals(type)) {//仪表读数 + double resultvalue = Double.parseDouble(value); + //String alarmlevel = "0";// '1:预警;2:一般;3:严重;4;危急', + SubstationDevice device = deviceMapper.selectById(deviceid); + if (ObjUtil.isEmpty(device.getEarlyMin()) || ObjUtil.isEmpty(device.getCriticalMax())) { + //未设置上下限,判定为正常 + result.put("isNormal", "1");//判定为正常 + } else { + if (NumberUtil.compare(resultvalue, device.getEarlyMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getEarlyMax()) < 0) { + result.put("alarmlevel", "1"); + result.put("isNormal", "0"); + } else if (NumberUtil.compare(resultvalue, device.getSameMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getSameMax()) < 0) { + result.put("alarmlevel", "2"); + result.put("isNormal", "0"); + } else if (NumberUtil.compare(resultvalue, device.getSeriousMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getSeriousMax()) < 0) { + result.put("alarmlevel", "3"); + result.put("isNormal", "0"); + } else if (NumberUtil.compare(resultvalue, device.getCriticalMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getCriticalMax()) < 0) { + result.put("alarmlevel", "4"); + result.put("isNormal", "0"); + } else { + result.put("isNormal", "1");//判定为正常 + } + } + + } else if ("infrared".equals(type)) { + //温度 最高温度,最低温度(以逗号分隔) + String highTemp = value.split(",")[0]; //最高温度 + double resultvalue = Double.parseDouble(highTemp); + //String alarmlevel = "0";// '1:预警;2:一般;3:严重;4;危急', + SubstationDevice device = deviceMapper.selectById(deviceid); + if (ObjUtil.isEmpty(device.getEarlyMin()) || ObjUtil.isEmpty(device.getCriticalMax())) { + //未设置上下限,判定为正常 + result.put("isNormal", "1");//判定为正常 + } else { + if (NumberUtil.compare(resultvalue, device.getEarlyMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getEarlyMax()) < 0) { + result.put("alarmlevel", "1"); + result.put("isNormal", "0"); + } else if (NumberUtil.compare(resultvalue, device.getSameMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getSameMax()) < 0) { + result.put("alarmlevel", "2"); + result.put("isNormal", "0"); + } else if (NumberUtil.compare(resultvalue, device.getSeriousMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getSeriousMax()) < 0) { + result.put("alarmlevel", "3"); + result.put("isNormal", "0"); + } else if (NumberUtil.compare(resultvalue, device.getCriticalMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getCriticalMax()) < 0) { + result.put("alarmlevel", "4"); + result.put("isNormal", "0"); + } else { + //判定为正常 + result.put("isNormal", "1"); + } + } + + } else if ("isolator".equals(type)) { + //刀闸状态 + if ("1".equals(value)) { + result.put("desc", "分"); + result.put("isNormal", "1"); + } + if ("2".equals(value)) { + result.put("desc", "合"); + result.put("isNormal", "1"); + } + if ("3".equals(value)) { + result.put("desc", "分位异常"); + result.put("isNormal", "0"); + } + if ("4".equals(value)) { + result.put("desc", "合位异常"); + result.put("isNormal", "0"); + } + + } else if ("yjsk".equals(type)) { + //刀闸状态 + if ("2".equals(value) || "4".equals(value)) { + result.put("isNormal", "0"); + } else { + result.put("isNormal", "1"); + } + } else if ("sound".equals(type)) {//声音 1 代表正常声音,2 代表异常声音 + double resultvalue = Double.parseDouble(value); + //String alarmlevel = "0";// '1:预警;2:一般;3:严重;4;危急', + SubstationDevice device = deviceMapper.selectById(deviceid); + if (ObjUtil.isEmpty(device.getEarlyMin()) || ObjUtil.isEmpty(device.getCriticalMax())) { + //未设置上下限,判定为正常 + result.put("isNormal", "1");//判定为正常 + } else { + if (NumberUtil.compare(resultvalue, device.getEarlyMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getCriticalMax()) < 0) { //异常 + result.put("isNormal", "0");//判定为异常 + result.put("alarmtype", "7");//<7>: = 声音超限制 + if (NumberUtil.compare(resultvalue, device.getEarlyMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getEarlyMax()) < 0) { + result.put("alarmlevel", "1"); + } else if (NumberUtil.compare(resultvalue, device.getSameMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getSameMax()) < 0) { + result.put("alarmlevel", "2"); + } else if (NumberUtil.compare(resultvalue, device.getSeriousMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getSeriousMax()) < 0) { + result.put("alarmlevel", "3"); + } else if (NumberUtil.compare(resultvalue, device.getCriticalMin()) >= 0 && NumberUtil.compare(resultvalue, + device.getCriticalMax()) < 0) { + result.put("alarmlevel", "4"); + } else { + result.put("alarmlevel", "4"); + } + } else if (NumberUtil.compare(resultvalue, device.getCriticalMax()) > 0) { + result.put("isNormal", "0");//判定为异常 + result.put("alarmtype", "5");//<5>: = 声音异常 + } else { + result.put("isNormal", "1");//判定为正常 + } + } + + } else if ("tx_pb".equals(type)) { + //缺陷判别 + if ("1".equals(value)) { + result.put("isNormal", "0"); + } else { + result.put("isNormal", "1"); + } + } else if ("light".equals(type)) { + if ("red_on".equals(value)) { + result.put("isNormal", "0"); + } else { + result.put("isNormal", "1"); + } + } else if ("switch".equals(type)) { + //switch,light + result.put("isNormal", "1");//正常 + } else if ("qrcode".equals(type)) { + // 实物 ID + } else { + if ("1".equals(value)) {//外观缺陷 + if ("ws_ywzc".equals(type) + || "fhz_f".equals(type) || "fhz_h".equals(type) + || "kgg_ybf".equals(type) || "kgg_ybh".equals(type) + || "kk_f".equals(type) || "kk_h".equals(type) + || "zsd_l".equals(type) || "zsd_m".equals(type) + || "hxq_gjzc".equals(type) || "ywzt_yfzc".equals(type) + || "bjzc".equals(type) || "xmbhzc".equals(type) + || "gzzc".equals(type) || "aqmzc".equals(type)) { + result.put("isNormal", "1");//正常 + } else { + result.put("isNormal", "0");//异常 + result.put("alarmtype", "6");//<6>: = 外观异常 + } + + } else { + result.put("isNormal", "1");//正常 + } + } + return result; + } + + /********************************** + * 用途说明: 生成报警记录(点位) + * 参数说明 taskResult + * 参数说明 map + * 返回值说明: boolean + ***********************************/ + private boolean createTaskAlarmLog(TaskResult taskResult, Map map) throws UnsupportedEncodingException { + log.info("===========================" + taskResult.toString()); + AlarmLog alarmLog = new AlarmLog(); + alarmLog.setId(IdUtil.fastSimpleUUID()); + String deviceid = taskResult.getDeviceId(); + SubstationDevice device = deviceMapper.selectById(deviceid); + String alarmDate = DateUtil.now(); + Substation substation = stationMapper.selectOne(new LambdaQueryWrapper(). + eq(Substation::getStationCode, device.getStationCode())); + alarmLog.setStationCode(device.getStationCode()); + alarmLog.setStationId(substation.getStationId()); + alarmLog.setStationName(substation.getStationName()); + alarmLog.setAreaId(taskResult.getAreaId()); + alarmLog.setAreaName(taskResult.getAreaName()); + alarmLog.setBayId(taskResult.getBayId()); + alarmLog.setBayName(taskResult.getBayName()); + alarmLog.setMainDeviceId(taskResult.getMainDeviceId()); + alarmLog.setMainDeviceName(taskResult.getMainDeviceName()); + alarmLog.setComponentId(taskResult.getComponentId()); + alarmLog.setComponentName(taskResult.getComponentName()); + alarmLog.setDeviceId(taskResult.getDeviceId()); + alarmLog.setDeviceName(taskResult.getDeviceName()); + alarmLog.setPatroldeviceCode(taskResult.getPatroldeviceCode()); + alarmLog.setPatroldeviceName(taskResult.getPatroldeviceName()); + alarmLog.setMaterialId(taskResult.getMaterialId()); + TaskTodo taskTodo = taskTodoMapper.selectById(taskResult.getTaskTodoId()); + alarmLog.setTaskId(taskTodo.getTaskId()); + alarmLog.setTaskPatrolledId(taskResult.getTaskTodoId()); + alarmLog.setTaskResultId(taskResult.getResultId()); + alarmLog.setTaskCode(taskResult.getTaskCode()); + alarmLog.setTaskName(taskResult.getTaskName()); + alarmLog.setTaskAlarmType("1"); //'1:巡视任务告警;2:静默任务告警,3:巡视设备告警', + //alarmLog.setMonitorType("");//非静默监视不填 + alarmLog.setAlarmType(ObjUtil.isNotEmpty(map.get("alarmtype")) ? map.get("alarmtype").toString() : "");//'字典表<1>: = 超温报警<2>: = 温升报警<3>: = 三相温差报警<4>: = 三相对比报警<5>: = 声音异常<6>: = 外观异常<7>: = 仪表越限报警<8>: = 仪表超量程报警<9>: = 仪表三相对比<10>: = 变位报警', + alarmLog.setAlarmLevel(ObjUtil.isNotEmpty(map.get("alarmlevel")) ? map.get("alarmlevel").toString() : "4");// '1:预警;2:一般;3:严重;4;危急', 待判断细分 + alarmLog.setRecognitionType(taskResult.getRecognitionType());//'1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6:闪烁检测11:局放超声波检测12:局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测', + alarmLog.setFileType(taskResult.getFileType());//'1:红外图谱;2:可见光照片;3:音频4:视频5:识别图片,存在多个文件,文件类型用逗号分隔', + //告警文件目录规范:地市名/区域名/变电站名/分类/年月 + String type = "缺陷";//缺陷、判别 + String alarmfilepath = String.format("%s/%s/%s/%s/%s/", + substation.getCityName(), + substation.getSectionName(), + substation.getStationName(), + type, + DateUtil.format(DateUtil.date(), "yyyyMM") + ); + log.info( + "============================================拷贝原图到报警目录下============================================"); + //拷贝原图到报警目录下 + String fullfilename = URLDecoder.decode(taskResult.getFilePath(), "utf-8"); + String alarmfullfilename = alarmfilepath + StrUtil.subAfter(fullfilename, "/", true); + FileUtil.copy(Config.getSnapFilePath() + fullfilename, Config.getAlarmFilePath() + alarmfullfilename, true); + alarmLog.setFilePath(URLEncoder.encode(alarmfullfilename, "utf-8"));//原图文件名称(含路径) + alarmLog.setDefectType(taskResult.getValueType());//'缺陷类别' + log.info( + "============================================拷贝识别图到报警目录下============================================"); + //拷贝识别图到报警目录下 + if (ObjUtil.isNotEmpty(taskResult.getDefectFilePath())) { + String fulldefectfilename = URLDecoder.decode(taskResult.getDefectFilePath(), "utf-8"); + String alarmfilename = StrUtil.replace(StrUtil.subAfter(fulldefectfilename, "/", true), "识别", "缺陷告警"); + String fullalarmfile = alarmfilepath + alarmfilename; + FileUtil.copy(Config.getSnapFilePath() + fulldefectfilename, Config.getAlarmFilePath() + fullalarmfile, + true); + alarmLog.setDefectFilePath(URLEncoder.encode(fullalarmfile, "utf-8"));//标注了识别框和信息 + } + log.info( + "============================================图形识别结果(值)============================================"); + alarmLog.setValue(taskResult.getValue());//图形识别结果(值) + alarmLog.setUnit(taskResult.getUnit()); + String alarminfo = ""; + String value = taskResult.getValue(); + String unit = ""; + if (StrUtil.isNotBlank(taskResult.getUnit())) { + unit = taskResult.getUnit(); + } + if ("meter".equals(taskResult.getValueType())) { + alarminfo = String.format("%s-%s的%s,识别结果为%s%s,判定为[%s]超限缺陷!", taskResult.getMainDeviceName(), + taskResult.getComponentName(), taskResult.getDeviceName(), value, + unit, taskResult.getDesc()); + } + if ("infrared".equals(taskResult.getValueType())) { + alarminfo = String.format("%s-%s的%s,识别温度:%s单位:%s,判定为[%s]超限缺陷!", taskResult.getMainDeviceName(), + taskResult.getComponentName(), taskResult.getDeviceName(), value, + unit, taskResult.getDesc()); + } + if ("sound".equals(taskResult.getValueType())) { + alarminfo = "声音检测异常!"; + } + if (StrUtil.isBlank(alarminfo)) { +// // 图像缺陷分析类型 +// List> pictureDefectAnalysisTypeList = sysDictionaryItemsService.getDeviceByType( +// "PictureDefectAnalysisType"); +// String valueType = taskResult.getValueType(); +// Set customList = pictureDefectAnalysisTypeList.stream().filter(c -> ObjectUtil.isNotEmpty(c.get( +// "custom1"))).map(c -> c.get("custom1").toString()).collect(Collectors.toSet()); +// for (String custom : customList) { +// if (JSONUtil.isTypeJSONArray(custom)) { +// JSONArray jsonArray = JSONUtil.parseArray(custom); +// for (int i = 0; i < jsonArray.size(); i++) { +// JSONObject jsonObject = jsonArray.getJSONObject(i); +// // 检查当前对象的key字段是否与目标key匹配 +// if (jsonObject.getStr("key").equals(valueType)) { +// // 如果匹配,取出对应的value字段的值 +// value = jsonObject.getStr("value"); +// taskResult.setValue(value); +// break; // 找到后跳出循环 +// } +// } +// } +// +// } + alarminfo = String.format("%s-%s的%s,识别结果为%s%s,判定为[%s]异常!", taskResult.getMainDeviceName(), + taskResult.getComponentName(), taskResult.getDeviceName(), value, unit + , taskResult.getDesc()); + } + alarmLog.setContent(alarminfo);//告警描述信息 + alarmLog.setAlarmDate(alarmDate);//发送报警时间 + alarmLog.setCheckFlag("0");//'0:未核查;1:已审核;2.已修正' + alarmLog.setRectangle(taskResult.getRectangle());//'格式:x1,y1,x2y2;x3,y3,x4,y4;多个报警框的左上角和右下角坐标' + alarmLog.setDatastatus("1"); //初始创建 + alarmLog.setLastmodifier("系统创建"); + alarmLog.setLastmodifydate(new Timestamp(DateUtil.current())); + this.save(alarmLog);//保存此报警数据 + LocalDateTime delayedAlarmDate = device.getDelayedAlarmDate(); + // 如果当前时间小于报警延迟时间则不报警 + if (delayedAlarmDate != null) { + LocalDateTime parse = LocalDateTimeUtil.parse(alarmDate, "yyyy-MM-dd HH:mm:ss"); + if (!parse.isAfter(delayedAlarmDate)) { + return true; + } + } + Map logMap = BeanUtil.beanToMap(alarmLog); + String stationId = alarmLog.getStationId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getStationId, stationId).eq(AlarmLog::getCheckFlag + , 0); + int count = this.count(queryWrapper); + logMap.put("alarmCount", count); + JSONObject jsonObject = new JSONObject(logMap); + String stationCode = substation.getStationCode(); + SysOrganization sysOrganization = + sysOrganizationMapper.selectOne(new LambdaUpdateWrapper().eq(SysOrganization::getOrgcode, stationCode)); + String parentid = sysOrganization.getParentid(); + jsonObject.putOpt("deviceid", jsonObject.getStr("deviceId")); + // if (ObjectUtil.isNotEmpty(map.get("patroldeviceCode")) && ObjectUtil.isNotEmpty(map.get( + // "patroldeviceChannelcode"))) { + // jsonObject.putOpt("channelId", map.get("patroldeviceChannelcode").toString()); + // jsonObject.putOpt("deviceId", map.get("patroldeviceCode").toString()); + // } + jsonObject.putOpt("channelId", taskResult.getPatroldeviceChannelcode()); + jsonObject.putOpt("deviceId", taskResult.getPatroldeviceCode()); + // 告警阈值 + jsonObject.putOpt("earlyMin", device.getEarlyMin()); + jsonObject.putOpt("earlyMax", device.getEarlyMax()); + jsonObject.putOpt("sameMin", device.getSameMin()); + jsonObject.putOpt("sameMax", device.getSameMax()); + jsonObject.putOpt("seriousMin", device.getSeriousMin()); + jsonObject.putOpt("seriousMax", device.getSeriousMax()); + jsonObject.putOpt("criticalMin", device.getCriticalMin()); + jsonObject.putOpt("criticalMax", device.getCriticalMax()); + log.info( + "============================================如果是站端则区域也需要发送告警============================================"); + // 如果是站端则区域也需要发送告警 + if (!"0".equals(parentid)) { + WebSocketServer.sendInfo(parentid, jsonObject.toString()); + } + WebSocketServer.sendInfo(stationId, jsonObject.toString()); + log.info( + "============================================发送告警============================================"); + // // 向算法管理平台发送告警信息 + // JSONObject alarmContent = new JSONObject(); + // alarmContent.putOnce("province_name", substation.getProvinceName()); + // alarmContent.putOnce("city_name", substation.getCityName()); + // alarmContent.putOnce("volt_level", substation.getVoltLevel()); + // alarmContent.putOnce("station_name", substation.getStationName()); + // alarmContent.putOnce("section_ip", substation.getStationIp()); + // alarmContent.putOnce("node_id", substation.getNodeId()); + // alarmContent.putOnce("msg_type", "alarm"); + // JSONArray alarmArray = new JSONArray(); + // JSONObject alarmObject = new JSONObject(); + // alarmObject.putOnce("bay_name", ""); + // alarmObject.putOnce("device_name", ""); + // alarmObject.putOnce("point_name", ""); + // alarmObject.putOnce("time", DateUtil.now()); + // + // // 获取坐标 + // String rectangle = taskResult.getRectangle(); + // JSONArray defectArray = new JSONArray(); + // JSONObject defectObject = new JSONObject(); + // defectObject.putOnce("type", alarmLog.getDefectType()); + // defectObject.putOnce("confidence", taskResult.getConf()); + // defectObject.putOnce("desc", alarmLog.getContent()); + // if (StrUtil.isNotBlank(rectangle)) { + // JSONArray rectangleArray = JSONUtil.parseArray(rectangle); + // JSONArray differentArray = new JSONArray(); + // for (int i = 0; i < rectangleArray.size(); i++) { + // JSONObject coorsObject = rectangleArray.getJSONObject(i); + // // 坐标数组 + // JSONArray coorsArray = coorsObject.getJSONArray("areas"); + // JSONObject different = new JSONObject(); + // for (int j = 0; j < coorsArray.size(); j++) { + // JSONObject dataObject = coorsArray.getJSONObject(j); + // for (String data : dataObject.keySet()) { + // different.putOnce(data + (j + 1), dataObject.getStr(data)); + // defectObject.putOpt(data + (j + 1), dataObject.getStr(data)); + // } + // } + // differentArray.add(different); + // } + // alarmObject.putOnce("different", differentArray); + // } else { + // alarmObject.putOnce("different", new JSONArray()); + // } + // defectArray.add(defectObject); + // alarmObject.putOnce("defect", defectArray); + // alarmArray.add(alarmObject); + // alarmContent.putOnce("alarm", alarmArray); + // + // // 原图 + // String picRaw = ""; + // if (ObjectUtil.isNotEmpty(taskResult.getFilePath())) { + // picRaw = URLDecoder.decode(taskResult.getFilePath(), "utf-8"); + // } + // alarmObject.putOnce("pic_raw", picRaw); + // // 识别图 + // String picDefect = ""; + // if (ObjectUtil.isNotEmpty(taskResult.getDefectFilePath())) { + // picDefect = URLDecoder.decode(taskResult.getDefectFilePath(), "utf-8"); + // } + // alarmObject.putOnce("pic_defect", picDefect); + // + // //TODO 缺少基准图、判别图 + // alarmObject.putOnce("pic_different", ""); + // alarmObject.putOnce("pic_diff_base", ""); + // //文件转化为图片 + // Image srcImg = null; + // try { + // if (StrUtil.isNotBlank(picDefect)) { + // String imagePath = httpServerConfig.getSnapFilePath() + picDefect; + // String ext = imagePath.substring(imagePath.lastIndexOf(".") + 1); + // if ("jpg".equals(ext) || "JPG".equals(ext)) { + // srcImg = ImageIO.read(new File(imagePath)); + // //获取图片的宽 + // int srcImgWidth = srcImg.getWidth(null); + // //获取图片的高 + // int srcImgHeight = srcImg.getHeight(null); + // alarmObject.putOnce("pic_width", srcImgWidth); + // alarmObject.putOnce("pic_height", srcImgHeight); + // } + // } else { + // alarmObject.putOnce("pic_width", 0); + // alarmObject.putOnce("pic_height", 0); + // } + // } catch (IOException e) { + // log.error("发生错误", e); + // throw new RuntimeException(e.getMessage()); + // } + //// mqttGateway.sendToMqtt( "nodes/java/user/alert/2", 2,alarmContent.toString()); + // + // // 区域巡视主机生成告警数据时,主动向上级系统上报告警并接收响应消息。 +// JSONObject alarmData = new JSONObject(); +// alarmData.set("patroldevice_name", alarmLog.getPatroldeviceName()); +// alarmData.set("patroldevice_code", alarmLog.getPatroldeviceCode()); +// alarmData.set("task_name", alarmLog.getTaskName()); +// alarmData.set("task_code", alarmLog.getTaskCode()); +// alarmData.set("device_name", alarmLog.getDeviceName()); +// alarmData.set("device_id", alarmLog.getDeviceId()); +// alarmData.set("alarm_level", alarmLog.getAlarmLevel()); +// alarmData.set("alarm_type", alarmLog.getAlarmType()); +// alarmData.set("recognition_type", alarmLog.getRecognitionType()); +// alarmData.set("defect_type", alarmLog.getDefectType()); +// alarmData.set("file_type", alarmLog.getFileType()); +// alarmData.set("file_path", alarmLog.getFilePath()); +// alarmData.set("value", alarmLog.getPatroldeviceName()); +// alarmData.set("value_unit", alarmLog.getValue()); +// alarmData.set("unit", alarmLog.getUnit()); +// alarmData.set("time", taskResult.getTime()); +// alarmData.set("link_point", taskResult.getDeviceId()); +// alarmData.set("task_patrolled_id", alarmLog.getTaskPatrolledId()); +// alarmData.set("content", alarmLog.getContent()); +// this.sendTaskData("62", "", alarmLog.getStationCode(), alarmData.toString()); + return true; + } + + public void sendTaskData(String Type, String Command, String Code, String Items) { + String xml = MyXmlUtil.getXml(httpServerConfig.getPatrolServerid(), parentConfig.getParentId(), Type + , Command, Code, Items); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getRequestSessionID()); + messageProtocol.setReceiverSerialNo(new Long(0L)); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if (BootNettyClientChannelCache.channelMapCache.size() > 0) { + for (Map.Entry entry : + BootNettyClientChannelCache.channelMapCache.entrySet()) { + BootNettyClientChannel bootNettyChannel = entry.getValue(); + if (bootNettyChannel != null && bootNettyChannel.getChannel().isOpen()) { + bootNettyChannel.getChannel().writeAndFlush(messageProtocol); + } else { + BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), + Integer.parseInt(parentConfig.getTcpPort()), parentConfig); + thread.start(); + } + } + } else { + BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), + Integer.parseInt(parentConfig.getTcpPort()), parentConfig); + thread.start(); + } + } + + /********************************** + * 用途说明: 生成报警记录(静默) + * 参数说明 jsonObject + * 返回值说明: boolean + ***********************************/ + @Override + public boolean createAlarm(JSONObject jsonObject) throws Exception { + JSONArray jsonArray = jsonObject.getJSONArray("resultList"); + if (jsonArray.size() > 0) { + JSONObject jsonResult = jsonArray.getJSONObject(0); + JSONArray results = jsonResult.getJSONArray("results"); + for (int i = 0; i < results.size(); i++) { + JSONObject result = results.getJSONObject(i); + if (result.getStr("type").equals("gzzc") || result.getStr("type").equals("aqmzc")) { + //工装和安全帽正常 + continue; + } + //正确 2000 图像数据错误 2001 算法分析失败 2002 + String code = result.getStr("code"); + if ("2000".equals(code)) { + Thread.sleep(1500);//延迟1.5秒 + AlarmLog alarmLog = new AlarmLog(); + alarmLog.setId(IdUtil.fastSimpleUUID()); + //摄像机ID + String deviceid = jsonObject.getStr("requestId"); + //摄像机信息 + SubstationPatroldevice patroldevice = patroldeviceMapper.selectById(deviceid); + String alarmDate = DateUtil.now(); + String monitorType = this.getSlienceType(result.getStr("type")); + LocalDateTime delayedAlarmDate = null; + String slienceParams = patroldevice.getSlienceParams(); + if (StrUtil.isNotEmpty(slienceParams)) { + JSONArray slienceArray = JSONUtil.parseArray(slienceParams); + //获取报警时间判断是否报警 + for (Object o : slienceArray) { + JSONObject silentData = JSONUtil.parseObj(o); + if (monitorType.equals(silentData.getStr("type"))) { + String delayedAlarmDateStr = silentData.getStr("delayed_alarm_date"); + // 将日期转换成yyyy-MM-dd HH:mm:ss格式 + delayedAlarmDate = LocalDateTimeUtil.parse(delayedAlarmDateStr, + DatePattern.NORM_DATETIME_PATTERN); + } + } + } + + Substation substation = stationMapper.selectOne(new LambdaQueryWrapper(). + eq(Substation::getStationCode, patroldevice.getStationCode())); + alarmLog.setStationCode(patroldevice.getStationCode()); + alarmLog.setStationId(substation.getStationId()); + alarmLog.setStationName(substation.getStationName()); + alarmLog.setAreaId(patroldevice.getAreaId()); + alarmLog.setAreaName(patroldevice.getAreaName()); + alarmLog.setBayId(""); + alarmLog.setBayName(""); + alarmLog.setMainDeviceId(""); + alarmLog.setMainDeviceName(""); + alarmLog.setComponentId(""); + alarmLog.setComponentName(patroldevice.getCustom2()); //监视位置 + alarmLog.setDeviceId(""); + alarmLog.setDeviceName(patroldevice.getCustom2());//静默监视点位 + alarmLog.setPatroldeviceCode(patroldevice.getPatroldeviceCode()); + alarmLog.setPatroldeviceName(patroldevice.getPatroldeviceName()); + alarmLog.setTaskName(result.getStr("desc") + "静默监视"); + alarmLog.setTaskAlarmType("2"); //'1:巡视任务告警;2:静默任务告警,3:巡视设备告警', + alarmLog.setMonitorType(result.getStr("type"));//监视类型 + alarmLog.setAlarmType("");//'字典表<1>: = 超温报警<2>: = 温升报警<3>: = 三相温差报警<4>: = 三相对比报警<5>: = 声音异常<6>: = + // 外观异常<7>: = 仪表越限报警<8>: = 仪表超量程报警<9>: = 仪表三相对比<10>: = 变位报警', + alarmLog.setAlarmLevel("2");// '1:预警;2:一般;3:严重;4;危急', 待判断细分 + alarmLog.setRecognitionType("");//'1:表计读取;2:位置状态识别;3:设备外观查看;4:红外测温;5:声音检测;6:闪烁检测11:局放超声波检测12 + // :局放地电压检测13:局放特高频检测101:环境温度检测102:环境湿度检测103:氧气浓度检测104SF6浓度检测', + alarmLog.setFileType(patroldevice.getVideoMode());//'1:红外图谱;2:可见光照片;3:音频4:视频5:识别图片,存在多个文件,文件类型用逗号分隔', + //告警文件目录规范:地市名/区域名/变电站名/分类/年月 + String type = "缺陷";//缺陷、判别 + String alarmfilepath = String.format("%s/%s/%s/%s/%s/", + substation.getCityName(), + substation.getSectionName(), + substation.getStationName(), + type, + DateUtil.format(DateUtil.date(), "yyyyMM") + ); + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg +// String alarmfilename = String.format("%s_%s_%s_%s_%s_%s.jpg", +// DateUtil.format(DateUtil.date(), "yyyyMMdd"), +// DateUtil.format(DateUtil.date(), "HHmmss"), +// patroldevice.getAreaName(), +// patroldevice.getPatroldeviceName(), +// patroldevice.getCustom2(), +// "原图" +// ); + String alarmfilename = String.format("%s_%s_%s.jpg", + patroldevice.getPatroldeviceId(), + patroldevice.getPatroldeviceCode(), + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ); + String resImageUrl = result.getStr("resImagePath"); + String fullfilename = URLDecoder.decode(resImageUrl.split("=")[1], "utf-8"); + String alarmfullfilename = alarmfilepath + alarmfilename; + + FileUtil.copy(Config.getSnapFilePath() + fullfilename, + Config.getAlarmFilePath() + alarmfullfilename, true); + alarmLog.setFilePath(URLEncoder.encode(alarmfullfilename, "utf-8"));//原图文件名称(含路径) + + String markedfilename = StrUtil.replace(alarmfullfilename, ".jpg", "判别.jpg"); + String markedfullfilename = Config.getAlarmFilePath() + markedfilename; + FileUtil.MarkImageRectangle(Config.getAlarmFilePath() + alarmfullfilename, result.getStr("pos"), + markedfullfilename); + //水印字体 + Font font = new Font("微软雅黑", Font.PLAIN, 40); + //水印图片色彩 + Color color1 = new Color(31, 183, 55); + FileUtil.addWaterMark(markedfullfilename, markedfullfilename, patroldevice.getCustom2(), 20, -40, + color1, font); + Color color2 = new Color(226, 7, 24); + String alertinfo = + result.getStr("desc") + " " + "置信度:" + result.getStr("conf") + " " + result.getStr( + "time"); + FileUtil.addWaterMark(markedfullfilename, markedfullfilename, alertinfo, 600, -40, color2, font); + + alarmLog.setDefectFilePath(URLEncoder.encode(markedfilename, "utf-8"));//标注了识别框和信息 + alarmLog.setDefectType(result.getStr("type"));//'缺陷类别' + alarmLog.setValue(result.getStr("value"));//图形识别结果(值) + alarmLog.setAlarmDate(alarmDate);//发送报警时间 + String alarminfo = getTemplateContent(result.getStr("type"), substation.getStationName(), + patroldevice.getAreaName(), patroldevice.getPatroldeviceName(), alarmLog.getAlarmDate(), + result.getStr("conf"), result.getStr("desc")); + alarmLog.setContent(alarminfo);//告警描述信息 + alarmLog.setCheckFlag("0");//'0:未核查;1:已审核;2.已修正' + alarmLog.setRectangle(JSONUtil.toJsonStr(result.getJSONArray("pos")));//'格式:x1,y1,x2y2;x3,y3,x4, + // y4;多个报警框的左上角和右下角坐标' + alarmLog.setDatastatus("1"); //初始创建 + alarmLog.setLastmodifier("系统创建"); + alarmLog.setLastmodifydate(new Timestamp(DateUtil.current())); + this.save(alarmLog);//保存此报警数据 + // 如果当前时间小于报警延迟时间则不报警(zhengsl) + if (delayedAlarmDate != null) { + LocalDateTime parse = LocalDateTimeUtil.parse(alarmDate, "yyyy-MM-dd HH:mm:ss"); + if (!parse.isAfter(delayedAlarmDate)) { + return true; + } + } + Map logMap = BeanUtil.beanToMap(alarmLog); + String stationId = alarmLog.getStationId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AlarmLog::getDatastatus, "1").eq(AlarmLog::getStationId, stationId).eq(AlarmLog::getCheckFlag, 0); + int count = this.count(queryWrapper); + logMap.put("alarmCount", count); + JSONObject jsonObject1 = new JSONObject(logMap); + String stationCode = substation.getStationCode(); + SysOrganization sysOrganization = + sysOrganizationMapper.selectOne(new LambdaUpdateWrapper().eq(SysOrganization::getOrgcode, stationCode)); + String parentid = sysOrganization.getParentid(); + String channelinfo = patroldevice.getChannelinfo(); + if (StrUtil.isNotBlank(channelinfo)) { + JSONArray jsonArray1 = JSONUtil.parseArray(channelinfo); + for (int j = 0; j < jsonArray1.size(); j++) { + JSONObject channelData = jsonArray1.getJSONObject(j); + // channel_type 1:可见光 2:红外 + if ("1".equals(channelData.getStr("channel_type"))) { + String fromDevice = channelData.getStr("from_device"); + // 1:摄像机 + if ("1".equals(fromDevice)) { + jsonObject1.putOpt("deviceId", patroldevice.getInternationalId()); + jsonObject1.putOpt("channelId", channelData.getStr("channel_code")); + } else { + // 2:来源录像机 + jsonObject1.putOpt("deviceId", channelData.getStr("recorder_code")); + jsonObject1.putOpt("channelId", channelData.getStr("recorder_channelcode")); + } + break; + } + } + + } + if (!"0".equals(parentid)) { + WebSocketServer.sendInfo(parentid, jsonObject1.toString()); + } + WebSocketServer.sendInfo(stationId, jsonObject1.toString()); + // 区域巡视主机生成告警数据时,主动向上级系统上报告警并接收响应消息。 郑顺利注释 ,暂时不用向主机发送消息 +// JSONObject alarmData = new JSONObject(); +// alarmData.set("patroldevice_name",alarmLog.getPatroldeviceName()); +// alarmData.set("patroldevice_code",alarmLog.getPatroldeviceCode()); +// alarmData.set("alarm_level",alarmLog.getAlarmLevel()); +// alarmData.set("monitor_type",alarmLog.getMonitorType()); +// alarmData.set("file_type",alarmLog.getFileType()); +// alarmData.set("file_path",alarmLog.getFilePath()); +// alarmData.set("time",alarmLog.getAlarmDate()); +// alarmData.set("content",alarmLog.getContent()); +// this.sendTaskData("63","",alarmLog.getStationCode(),alarmData.toString()); + } + } + + } + + return true; + } + + /********************************** + * 用途说明: 获取模板内容 + * 参数说明 + * 返回值说明: java.lang.String + ***********************************/ + private String getTemplateContent(String type, String stationName, String areaName, String patrolDeviceName, + String time, String value, String desc) { + String templateContent = ""; + switch (type) { + // 未戴安全帽 + case "wcaqm": + templateContent = alarmTemplateConfig.getHelmetCamera(); + break; + // 未穿工装 + case "wcgz": + templateContent = alarmTemplateConfig.getClothCamera(); + break; + // 人员聚集、徘徊 + case "ryjjph": + templateContent = alarmTemplateConfig.getIntensiveCamera(); + break; + // 烟火 + case "hzyw": + templateContent = alarmTemplateConfig.getFireCamera(); + break; + // 渗漏油 + case "sly_dmyw": + templateContent = alarmTemplateConfig.getLeakageCamera(); + break; + // 人员进入 + case "person_enter": + templateContent = alarmTemplateConfig.getpersonEnter(); + break; + // 人员吸烟 + case "xy": + templateContent = alarmTemplateConfig.getpersonSmoke(); + break; + } + if (StrUtil.isNotBlank(templateContent)) { + return String.format(templateContent, stationName, areaName, patrolDeviceName, time, value, desc); + } else { + return ""; + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/DefectDeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/DefectDeviceServiceImpl.java new file mode 100644 index 0000000..f0f455f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/DefectDeviceServiceImpl.java @@ -0,0 +1,138 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.patroltask.domain.DefectDevice; +import com.yfd.platform.modules.patroltask.mapper.DefectDeviceMapper; +import com.yfd.platform.modules.patroltask.service.IDefectDeviceService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务实现类 + *

+ * + * @author zhengsl + * @since 2024-03-28 + */ +@Service +public class DefectDeviceServiceImpl extends ServiceImpl implements IDefectDeviceService { + + @Resource + private ISubstationDeviceService substationDeviceService; + @Resource + private DefectDeviceMapper defectDeviceMapper; + + /********************************** + * 用途说明: 查询缺陷数据 + * 参数说明 page + * 参数说明 stationId 变电站id + * 参数说明 areaId 区域id + * 参数说明 bayId 间隔id + * 参数说明 taskName 任务名称 + * 参数说明 deviceName 点位名称 + * 参数说明 patroldeviceName 设备名称 + * 参数说明 componentName 部件名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 isDefectFlag 是否归入 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + @Override + public ResponseResult getDefectDeviceList(Page> page, String stationCode, String areaId, + String bayId, String taskName, String deviceName, + String patroldeviceName, String componentName, String recognitionType, + String isDefectFlag, String startDate, String endDate) { + String startFormat = ""; + String endFormat = ""; + if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) { + + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + + } + Page> mapPage = defectDeviceMapper.getDefectDeviceList(page, stationCode, areaId, bayId, + taskName, deviceName, patroldeviceName, componentName, recognitionType, isDefectFlag, startFormat, + endFormat); + List> records = mapPage.getRecords(); + records.forEach(m -> { + String flag = m.get("flag").toString(); + String valid = ObjectUtil.isNotEmpty(m.get("valid1")) ? m.get("valid1").toString() : ""; + if ("4".equals(flag)) { + m.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + m.put("analysisResult", "采集失败"); + } else if ("2".equals(valid)) { + m.put("analysisResult", "异常"); + } + }); + mapPage.setRecords(records); + return ResponseResult.successData(mapPage); + } + + /********************************** + * 用途说明: 缺陷信息归入 + * 参数说明 ids id集合 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean setDefectDevice(String ids) { + if (StrUtil.isBlank(ids)) { + return false; + } + List idList = StrUtil.split(ids, ","); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.in(DefectDevice::getDefectId, idList).ne(DefectDevice::getIsDefectFlag, "1").set(DefectDevice::getIsDefectFlag, "1"); + return this.update(updateWrapper); + } + + /********************************** + * 用途说明: 根据id获取缺陷信息 + * 参数说明 id id + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + @Override + public ResponseResult getDefectDeviceById(String id) { + DefectDevice defectDevice = this.getById(id); + return ResponseResult.successData(defectDevice); + } + + /********************************** + * 用途说明: 缺陷信息删除 + * 参数说明 ids id集合 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteDefectDevice(String ids) { + if (StrUtil.isBlank(ids)) { + return false; + } + List idList = StrUtil.split(ids, ","); + return this.removeByIds(idList); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/DockTaskServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/DockTaskServiceImpl.java new file mode 100644 index 0000000..8aa0d5c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/DockTaskServiceImpl.java @@ -0,0 +1,295 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.nettyudpserver.NettyUdpServer; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.service.DockTaskService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.HttpRESTfulUtils; +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @Date: 2024/7/5 13:52 + * @Description: + */ +@Service +@Transactional +@Slf4j +public class DockTaskServiceImpl implements DockTaskService { + + @Resource + private ITaskTodoService taskTodoService; + @Resource + private ISubstationDeviceService substationDeviceService; + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + @Resource + private HttpRESTfulUtils httpUtil; + + /********************************** + * 用途说明: 保存无人机文件 + * 参数说明 jobId + * 参数说明 fileName + * 参数说明 url + * 返回值说明: void + ***********************************/ + @Override + public ResponseResult saveMediaFile(String jobId, String fileName, String url) throws Exception { + // 判断当前jobId是否存在 + TaskTodo taskTodo = taskTodoService.getById(jobId); + if (taskTodo == null) { + return ResponseResult.error(); + } + String[] split = fileName.replace(".jpeg", "").split("_"); + if (split.length <= 0) { + return ResponseResult.error(); + } + String deviceName = split[split.length - 1]; + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskResult::getDeviceName, deviceName).eq(TaskResult::getTaskTodoId, jobId); + List taskResultList = taskResultMapper.selectList(queryWrapper); + if (taskResultList.size() <= 0) { + return ResponseResult.error(); + } + TaskResult taskResult = taskResultList.get(0); + String flag = taskResult.getFlag(); + if (!"0".equals(flag)) { + return ResponseResult.success(); + } + String filetype = "CCD"; + String fileType = taskResult.getFileType(); + if (ObjectUtil.isNotEmpty(fileType)) { + switch (fileType) { + case "1": + filetype = "FIR"; + break; + case "3": + filetype = "Audio"; + break; + case "4": + filetype = "Video"; + break; + default: + filetype = "CCD"; + break; + } + } + String filepath = String.format("%s/%s/%s/%s/%s/%s/", + taskResult.getStationCode(), + DateUtil.format(DateUtil.date(), "yyyy"), + DateUtil.format(DateUtil.date(), "MM"), + DateUtil.format(DateUtil.date(), "dd"), + taskResult.getTaskCode(), + filetype + ); + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg +// String filename = String.format("%s_%s_%s_%s_%s_%s.jpg", +// DateUtil.format(DateUtil.date(), "yyyyMMdd"), +// DateUtil.format(DateUtil.date(), "HHmmss"), +// taskResult.getBayName(), +// taskResult.getMainDeviceName(), +// taskResult.getDeviceName(), +// "原图" +// ); + String filename = String.format("%s_%s_%s.jpg", + taskResult.getDeviceId(), + taskResult.getPatroldeviceCode(), + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ); + FileUtil.downloadFile(url, httpServerConfig.getSnapFilePath() + filepath, filename); + Thread.sleep(3000); + String fullfilename = filepath + filename; + //转码存储 + fullfilename = URLEncoder.encode(fullfilename, "utf-8"); + taskResult.setFilePath(fullfilename); + Integer integer = taskResultMapper.selectCount(new LambdaQueryWrapper().eq(TaskResult::getTaskTodoId, taskTodo.getTaskTodoId()).ne(TaskResult::getFlag, "0")); + taskTodoService.updateTaskResultStatus(taskResult.getTaskTodoId(), taskResult.getResultId(), fullfilename, "1"); + log.info("------------------总数" + taskTodo.getDeviceSumnum() + "------当前巡视数量" + integer); + if (integer == (taskTodo.getDeviceSumnum() - 1)) { + // 下载完图片调用分析主机 + new Thread(() -> { + try { + this.callPicAnalyse(taskTodo); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + ).start(); + } + + + return ResponseResult.success(); + } + + @Override + public void sendTaskResultData(String taskTodoId) throws Exception { + TaskTodo taskTodo = taskTodoService.getById(taskTodoId); + this.callPicAnalyse(taskTodo); + } + + /********************************** + * 用途说明: 调用分析主机 + * 参数说明 taskResult + * 参数说明 taskTodo + * 返回值说明: void + ***********************************/ + private void callPicAnalyse(TaskTodo taskTodo) throws Exception { + List taskResults = taskResultMapper.selectList(new LambdaQueryWrapper().eq(TaskResult::getTaskTodoId, taskTodo.getTaskTodoId())); + for (TaskResult taskResult : taskResults) { + Thread.sleep(10000); + String flag = taskResult.getFlag(); + if ("4".equals(flag)) { + continue; + } + String deviceId = taskResult.getDeviceId(); + //排除当前时刻需要检修的巡视点 + int count = taskTodoService.queryExaminePlan(deviceId, taskTodo.getPlanStartTime()); + if (count > 0) { + //更新当前记录状态,修改为 3:设备检修中 + taskTodoService.updateTaskResultStatus(taskTodo.getTaskTodoId(), taskResult.getResultId(), + "", "3"); + //跳过设备检修区间对应的巡视点 + continue; + } + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + //获取typelist 识别类型 + String pictureAnalysisTypeList = substationDevice.getPictureAnalysisTypeList(); + JSONArray typelist = new JSONArray(); + String analysePort = httpServerConfig.getAnalysePort11(); + JSONObject callresult = null; + JSONObject customParams = new JSONObject(); + if (StrUtil.isNotEmpty(pictureAnalysisTypeList)) { + JSONObject jobj = JSON.parseObject(pictureAnalysisTypeList); + if (ObjectUtil.isNotEmpty(jobj.getString("PictureAnalysisType"))) { +// if ("meter".equals(jobj.getString("PictureAnalysisType"))) { +// typelist.add(substationDevice.getOutsideFeature()); +// } else { +// typelist.add(jobj.getString("PictureAnalysisType")); +// } + if ("meter".equals(jobj.getString("PictureAnalysisType"))) { + customParams.put("meterType", substationDevice.getOutsideFeature()); + } + typelist.add(jobj.getString("PictureAnalysisType")); + } + + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDefectAnalysisType"))) { + analysePort = httpServerConfig.getAnalysePort12(); + JSONArray typeresult = new JSONArray(); + String[] PictureDefectAnalysisTypes = jobj.getString("PictureDefectAnalysisType").split(","); + for (String pictureDefectAnalysisType : PictureDefectAnalysisTypes) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId, "fedeb076ef96422eee01c88d7f7efd4e").eq(SysDictionaryItems::getItemCode, pictureDefectAnalysisType); //缺陷类别字典 + queryWrapper.select(SysDictionaryItems::getItemCode, + SysDictionaryItems::getDictName, SysDictionaryItems::getCustom1, + SysDictionaryItems::getCustom2); + SysDictionaryItems item = sysDictionaryItemsService.getOne(queryWrapper); + if (StrUtil.isNotEmpty(item.getCustom1()) && JSONUtil.isTypeJSONArray(item.getCustom1())) { + //确认一下是否是这个字段 + typeresult.addAll(JSONUtil.parseArray(item.getCustom1())); + } + + } + if (typeresult.size() > 0) { + //缺陷识别结果类型 + customParams.put("defectresulttypes", typeresult); + } + List pictureType = StrUtil.split(jobj.getString( + "PictureDefectAnalysisType"), ","); + List> pictureDefectAnalysisType = + sysDictionaryItemsService.getDeviceByType("PictureDefectAnalysisType"); + // 获取到子项 + List itemCodeList = + pictureDefectAnalysisType.stream().filter(p -> pictureType.contains(p.get("itemcode").toString())).map(m -> m.get("custom1").toString()).collect(Collectors.toList()); + for (String code : itemCodeList) { + JSONArray jsonArray = JSONUtil.parseArray(code); + List keyList = + jsonArray.stream().map(j -> JSONUtil.parseObj(j).getStr("key")).collect(Collectors.toList()); + typelist.addAll(keyList); + } + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDiscriminateAnalysisType"))) { + typelist.add(jobj.getString("PictureDiscriminateAnalysisType")); + analysePort = httpServerConfig.getAnalysePort12(); + } + } + if (typelist.size() == 0) { + throw new Exception("未设置识别方式,不能进行图形识别!"); + } + + // 1、 调用图片识别算法,进行图形识别 + //表计类型 + if (ObjectUtil.isNotEmpty(substationDevice.getMeterType())) { + customParams.put("meterType", substationDevice.getMeterType()); + } + if (ObjectUtil.isNotEmpty(substationDevice.getOutsideAngle())) { + if (JSONUtil.isTypeJSONArray(substationDevice.getOutsideAngle())) { + JSONArray jsonArray = JSONUtil.parseArray(substationDevice.getOutsideAngle()); + customParams.put("TempAngleList", jsonArray); + } + + } + + + //基准图 + if (ObjUtil.isNotEmpty(taskResult.getPatroldeviceBaseimage())) { + String path = URLEncoder.encode(taskResult.getPatroldeviceBaseimage(), "utf-8"); + String imageurl = String.format("http://%s:%s/%s/snapNormalFile?filename=%s", + httpServerConfig.getPatrolIp(), httpServerConfig.getPatrolPort(), + httpServerConfig.getPatrolAppname(), + path); + customParams.put("imageNormalUrlPath", imageurl); + } + //有效识别区域 + if (ObjUtil.isNotEmpty(taskResult.getPatroldeviceBaseimage())) { + customParams.put("effectiveregion", taskResult.getPatroldeviceBaseimage()); + } + log.info("==============================typelist=======================" + typelist.toString()); + log.info("customParams:" + JSONUtil.toJsonStr(customParams)); + String imgDecode = URLDecoder.decode(taskResult.getFilePath(), "utf-8"); + callresult = httpUtil.callPicAnalyse(taskResult.getTaskTodoId(), taskResult.getResultId(), JSONUtil.toJsonStr(typelist), JSONUtil.toJsonStr(customParams), imgDecode); + +// if (callresult != null) { +// String code = callresult.get("code").toString(); +// if ("-2".equals(code) || "201".equals(code)) { +// // 设置请求分析主机调用失败的任务为巡视失败 +// taskTodoService.setTaskResultAndTodo(taskTodo.getStationCode(), taskResult.getResultId(), +// taskTodo.getDeviceSumnum(), taskTodo.getTaskTodoId()); +// } +// } + } + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/ExaminePlanServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/ExaminePlanServiceImpl.java new file mode 100644 index 0000000..256900a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/ExaminePlanServiceImpl.java @@ -0,0 +1,132 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.SubstationBay; +import com.yfd.platform.modules.basedata.domain.SubstationComponent; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationMaindevice; +import com.yfd.platform.modules.basedata.mapper.*; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; +import com.yfd.platform.modules.patroltask.mapper.ExaminePlanMapper; +import com.yfd.platform.modules.patroltask.service.IExaminePlanService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + *

+ * 检修计划表 服务实现类 + *

+ * + * + * @since 2023-04-08 + */ +@Service +public class ExaminePlanServiceImpl extends ServiceImpl implements IExaminePlanService { + + @Resource + private SubstationMapper substationMapper; + + @Resource + private SubstationBayMapper substationBayMapper; + + @Resource + private SubstationMaindeviceMapper substationMaindeviceMapper; + + @Resource + private SubstationComponentMapper substationComponentMapper; + + @Resource + private SubstationDeviceMapper substationDeviceMapper; + /********************************** + * 用途说明: 根据Id查询关联信息 + * 参数说明 id + * 参数说明 deviceLevel + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getBindInfoById(String id, String deviceLevel) { + ExaminePlan examinePlan = this.getById(id); + String deviceList = examinePlan.getDeviceList(); +// String[] split = deviceList.split(","); + List> mapList = new ArrayList<>(); + if (StrUtil.isBlank(deviceList)) { + return mapList; + } + switch (deviceLevel) { + case "1": + + // for (String bayId : split) { + // SubstationBay substationBay = substationBayMapper.selectById(bayId); + // Map map = BeanUtil.beanToMap(substationBay); + // mapList.add(map); + // } + mapList.addAll(substationBayMapper.selectMaps(new LambdaQueryWrapper().in(SubstationBay::getBayId, StrUtil.split(deviceList, ",")))); + break; + case "2": + mapList.addAll(substationMaindeviceMapper.selectMaps(new LambdaQueryWrapper().in(SubstationMaindevice::getMainDeviceId, StrUtil.split(deviceList, ",")))); + + // for (String mainId : split) { + // SubstationMaindevice substationMaindevice = substationMaindeviceMapper + // .selectById(mainId); + // Map map = BeanUtil.beanToMap(substationMaindevice); + // mapList.add(map); + // } + break; + case "3": +// for (String deviceId : split) { +// SubstationDevice substationDevice = substationDeviceMapper.selectById(deviceId); +// Map map = BeanUtil.beanToMap(substationDevice); +// mapList.add(map); +// } + mapList.addAll(substationDeviceMapper.selectMaps(new LambdaQueryWrapper().in(SubstationDevice::getDeviceId, StrUtil.split(deviceList, ",")))); + break; + case "4": +// for (String componentId : split) { +// SubstationComponent substationComponent = substationComponentMapper.selectById(componentId); +// Map map = BeanUtil.beanToMap(substationComponent); +// mapList.add(map); +// } + mapList.addAll(substationComponentMapper.selectMaps(new LambdaQueryWrapper().in(SubstationComponent::getComponentId, StrUtil.split(deviceList, ",")))); + break; + } + return mapList; + } + + /********************************** + * 用途说明: 根据Id查询检修计划绑定点位 + * 参数说明 id 检修计划id + * 返回值说明: java.util.List + ***********************************/ + @Override + public List getBindExamineDevice(String id) { + ExaminePlan examinePlan = this.getById(id); + String deviceStr = examinePlan.getDeviceList(); + if (StrUtil.isBlank(deviceStr)) { + return null; + } + List ids = Arrays.asList(deviceStr.split(",")); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (1 == examinePlan.getDeviceLevel()) { + queryWrapper.in(SubstationDevice::getBayId, ids); + } + if (2==examinePlan.getDeviceLevel()) { + queryWrapper.in(SubstationDevice::getMainDeviceId, ids); + } + if (3==examinePlan.getDeviceLevel()) { + queryWrapper.in(SubstationDevice::getDeviceId, ids); + } + if (4==examinePlan.getDeviceLevel()) { + queryWrapper.in(SubstationDevice::getComponentId, ids); + } + List deviceList = substationDeviceMapper.selectList(queryWrapper); + return deviceList; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/QuartzMultiTaskManage.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/QuartzMultiTaskManage.java new file mode 100644 index 0000000..100d563 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/QuartzMultiTaskManage.java @@ -0,0 +1,198 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.quartz.impl.DirectSchedulerFactory; +import org.quartz.simpl.RAMJobStore; +import org.quartz.simpl.SimpleThreadPool; +import org.quartz.spi.JobStore; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @date 2023-04-28 + */ +@Slf4j +@Component +public class QuartzMultiTaskManage { + //巡视任务 + private static final String JOB_NAME = "PATROLTASK_"; + + private static Map schedulers = new HashMap(); + //private static SimpleThreadPool threadPool = new SimpleThreadPool(20, Thread.NORM_PRIORITY); + /** + * 添加一个job + * + * @param todoJob / + */ + public void addJob(TaskTodo todoJob) { + String statisoncode=todoJob.getStationCode(); + Scheduler statisonScheduler=null; + try { + if(ObjectUtil.isEmpty(schedulers.get(statisoncode))){ + DirectSchedulerFactory schedulerFactory = DirectSchedulerFactory.getInstance(); + SimpleThreadPool threadPool = new SimpleThreadPool(1, Thread.NORM_PRIORITY); + threadPool.initialize(); + JobStore jobStore = new RAMJobStore(); + schedulerFactory.createScheduler(statisoncode, statisoncode, threadPool, jobStore); + statisonScheduler=schedulerFactory.getScheduler(statisoncode); + schedulers.put(statisoncode,statisonScheduler); + }else{ + statisonScheduler=(Scheduler) schedulers.get(statisoncode); + } + // 构建job信息 任务按变电站分组 + JobDetail jobDetail = JobBuilder.newJob(TodoTaskJob.class). + withIdentity(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()).build(); + + Date plandate= DateUtil.parse(todoJob.getPlanStartTime(),"yyyy-MM-dd HH:mm:ss"); + // 创建触发器实例 + Trigger trigger = TriggerBuilder.newTrigger() + .withIdentity(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()) + .startAt(plandate) // 设置开始时间为指定的日期时间 + .endAt(null) // 设置结束时间为 null + .withSchedule(SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(0) // 设置重复次数为 0 + .withIntervalInSeconds(0) // 设置重复时间间隔为 0 + ) + .build(); + + trigger.getJobDataMap().put(TaskTodo.JOB_KEY, todoJob); + statisonScheduler.scheduleJob(jobDetail, trigger); + statisonScheduler.start(); + } catch (Exception e) { + log.error("添加定时任务失败", e); + throw new RuntimeException("添加定时任务失败"); + } + + + } + + /** + * 删除一个job + * + * @param todoJob / + */ + public void deleteJob(TaskTodo todoJob) { + String statisoncode=todoJob.getStationCode(); + Scheduler statisonScheduler=(Scheduler) schedulers.get(statisoncode); + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + statisonScheduler.pauseJob(jobKey); + statisonScheduler.deleteJob(jobKey); + } catch (Exception e) { + log.error("删除定时任务失败", e); + throw new RuntimeException("删除定时任务失败"); + } + } + + /** + * 中断一个执行中的任务 + * + * @param todoJob / + */ + public void interruptJob(TaskTodo todoJob) { + String statisoncode=todoJob.getStationCode(); + Scheduler statisonScheduler=(Scheduler) schedulers.get(statisoncode); + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + statisonScheduler.interrupt(jobKey); + } catch (Exception e) { + log.error("中断正在执行中的任务", e); + throw new RuntimeException("中断正在执行中的任务"); + } + } + + /** + * 恢复一个job + * + * @param todoJob / + */ + public void resumeJob(TaskTodo todoJob) { + String statisoncode=todoJob.getStationCode(); + Scheduler statisonScheduler=(Scheduler) schedulers.get(statisoncode); + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + Trigger trigger = + statisonScheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(todoJob); + } + JobKey jobKey = JobKey.jobKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + statisonScheduler.resumeJob(jobKey); + } catch (Exception e) { + log.error("恢复定时任务失败", e); + throw new RuntimeException("恢复定时任务失败"); + } + } + + /** + * 立即执行job + * + * @param todoJob / + */ + public void runJobNow(TaskTodo todoJob) { + String statisoncode=todoJob.getStationCode(); + Scheduler statisonScheduler=(Scheduler) schedulers.get(statisoncode); + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + Trigger trigger = + statisonScheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(todoJob); + } + JobDataMap dataMap = new JobDataMap(); + dataMap.put(TaskTodo.JOB_KEY, todoJob); + JobKey jobKey = JobKey.jobKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + if(DateUtil.compare(DateUtil.parse(todoJob.getPlanStartTime(),"yyyy-MM-dd HH:mm:ss"),DateUtil.date())>0){ + statisonScheduler.triggerJob(jobKey, dataMap); + } + } catch (Exception e) { + log.error("定时任务执行失败", e); + throw new RuntimeException("定时任务执行失败"); + } + } + + /** + * 暂停一个job + * + * @param todoJob / + */ + public void pauseJob(TaskTodo todoJob) { + String statisoncode=todoJob.getStationCode(); + Scheduler statisonScheduler=(Scheduler) schedulers.get(statisoncode); + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + todoJob.getTaskTodoId(),todoJob.getStationCode()); + statisonScheduler.pauseJob(jobKey); + } catch (Exception e) { + log.error("定时任务暂停失败", e); + throw new RuntimeException("定时任务暂停失败"); + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskDologServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskDologServiceImpl.java new file mode 100644 index 0000000..30f3484 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskDologServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.patroltask.domain.TaskDolog; +import com.yfd.platform.modules.patroltask.mapper.TaskDologMapper; +import com.yfd.platform.modules.patroltask.service.ITaskDologService; +import org.springframework.stereotype.Service; + +/** + *

+ * 巡视任务执行日志 服务实现类 + *

+ * + * + * @since 2023-05-03 + */ +@Service +public class TaskDologServiceImpl extends ServiceImpl implements ITaskDologService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskServiceImpl.java new file mode 100644 index 0000000..c77b003 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskServiceImpl.java @@ -0,0 +1,599 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.mapper.*; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SecurityUtils; +import org.quartz.TriggerUtils; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +/** + *

+ * 巡视任务表 服务实现类 + *

+ * + * @since 2023-04-08 + */ +@Service +@Transactional +public class TaskServiceImpl extends ServiceImpl implements ITaskService { + + @Resource + private SubstationMapper substationMapper; + + @Resource + private SubstationBayMapper substationBayMapper; + + @Resource + private SubstationMaindeviceMapper substationMaindeviceMapper; + + @Resource + private SubstationComponentMapper substationComponentMapper; + + @Resource + private SubstationDeviceMapper substationDeviceMapper; + + @Resource + private TaskTodoMapper taskTodoMapper; + + @Resource + private TaskMapper taskMapper; + + @Resource + private TaskResultMapper taskResultMapper; + + @Resource + private QuartzMultiTaskManage quartztaskManage; + @Resource + private LinkageSignalMapper linkageSignalMapper; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + + /********************************** + * 用途说明: 根据变电站Id查询巡视任务 + * 参数说明 stationId + * 返回值说明: java.util.List + ***********************************/ + @Override + public List getTaskByStationId(String stationId) { + Substation substation = substationMapper.selectById(stationId); + if (substation == null) { + return null; + } + String stationCode = substation.getStationCode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Task::getStationCode, stationCode).eq(Task::getDatastatus, "2").eq(Task::getIsenable, "1"); + return this.list(queryWrapper); + } + + /********************************** + * 用途说明: 根据Id查询关联信息 + * 参数说明 taskId 任务id + * 参数说明 deviceLevel 任务类型 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getBindInfoById(String id, String deviceLevel) { + Task task = this.getById(id); + String deviceList = task.getDeviceList(); + + // String[] split = deviceList.split(","); + List> mapList = new ArrayList<>(); + if (StrUtil.isBlank(deviceList)) { + return mapList; + } + switch (deviceLevel) { + case "1": + mapList.addAll(substationBayMapper.selectMaps(new LambdaQueryWrapper().in(SubstationBay::getBayId, StrUtil.split(deviceList, ",")))); + break; + case "2": + mapList.addAll(substationMaindeviceMapper.selectMaps(new LambdaQueryWrapper().in(SubstationMaindevice::getMainDeviceId, StrUtil.split(deviceList, ",")))); + break; + case "3": + mapList.addAll(substationDeviceMapper.selectByIds(deviceList)); + break; + case "4": + mapList.addAll(substationComponentMapper.selectMaps(new LambdaQueryWrapper().in(SubstationComponent::getComponentId, StrUtil.split(deviceList, ",")))); + break; + } + return mapList; + } + + /********************************** + * 用途说明: 新增巡视任务 + * 参数说明 task 巡视任务 + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean addTask(Task task) { + String id = IdUtil.fastSimpleUUID(); + task.setTaskId(id); + task.setLastmodifier(SecurityUtils.getCurrentUsername()); + task.setLastmodifydate(new Timestamp(DateUtil.current())); + task.setIsenable("0"); + task.setDatastatus("1"); + return this.save(task); + } + + /********************************** + * 用途说明: 分页查询巡视任务 + * 参数说明 page 分页对象 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 参数说明 type 巡视类型 + * 参数说明 taskName 任务名称 + * 参数说明 enable 是否启用 + * 参数说明 taskType 任务类型 + * 参数说明 taskTodoType 任务执行方式 + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getTaskList(Page> page, String startDate, String endDate, + String type, String taskName, String enable, String taskType, + String taskTodoType) throws ParseException { + Page> mapPage = taskMapper.getTaskList(page, startDate, endDate, type, taskName, enable, + taskType, taskTodoType); + return mapPage; + } + + /********************************** + * 用途说明: 根据任务创建待执行计划列表 + * 参数说明 task + * 返回值说明: void + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public TaskTodo createRunNowTask(Task task) throws ParseException { + // 执行类型 + String taskTodoType = task.getTaskTodoType(); + //立即执行 + if ("1".equals(taskTodoType)) { + Calendar calendar = Calendar.getInstance(); + String planStartTime = DateUtil.formatDateTime(calendar.getTime()); + //避免重复执行冲突 + setToDoTask(task, planStartTime); + } + TaskTodo taskTod = taskTodoMapper.selectList(new LambdaQueryWrapper().eq(TaskTodo::getTaskId, + task.getTaskId()).orderByDesc(TaskTodo::getPlanStartTime)).get(0); + return taskTod; + } + + /********************************** + * 用途说明: 发送机巢任务 + * 参数说明 task + * 返回值说明: com.alibaba.fastjson.JSONObject + ***********************************/ + private com.alibaba.fastjson.JSONObject sendDockTask(Task task, String planStartTime) { + String taskTodoType = task.getTaskTodoType(); + //TODO 向机巢发送任务 + String api = "wayline/api/v1/myWayLine/flight-tasks"; + if (StrUtil.isNotBlank(httpServerConfig.getDockServerApp())) { + api = httpServerConfig.getDockServerApp() + "/" + api; + } + Map param = new HashMap<>(); + JSONObject jsonObject = new JSONObject(); + String begin_time = null; + jsonObject.putOnce("name", task.getTaskName()); + int type = 0; + List dateList = new ArrayList<>(); + if (!"1".equals(taskTodoType)) { + type = -1; + // 将字符串解析为DateTime对象 + DateTime dateTime = DateUtil.parse(planStartTime); + + // 创建日期格式器 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + + // 分别格式化日期和时间 + dateList.add(dateFormat.format(dateTime)); + begin_time = timeFormat.format(dateTime); + } + jsonObject.putOnce(SystemCode.ITEM_BEGIN_TIME.getCode(), begin_time); + if (dateList.size() > 0) { + jsonObject.putOnce("day_list", dateList); + } else { + jsonObject.putOnce("day_list", null); + } + jsonObject.putOnce("task_type", type); + String dockInfo = task.getDockInfo(); + jsonObject.putAll(JSONUtil.parseObj(dockInfo)); + param.put("params", jsonObject); + return httpRESTfulUtils.sendHttpPost("json", + httpServerConfig.getDockServerIp(), httpServerConfig.getDockServerPort(), "", api, param, null); + } + + /********************************** + * 用途说明: 根据任务创建待执行计划列表 + * 参数说明 task + * 返回值说明: void + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public String createTodoTaskList(Task task) throws ParseException { + + // 执行类型 + String taskTodoType = task.getTaskTodoType(); + + if ("2".equals(taskTodoType)) { //定期执行 + String planStartTime = task.getFixedStartTime();//固定时间 + if (StrUtil.isNotEmpty(planStartTime)) { + return setToDoTask(task, planStartTime); + } + } + if ("3".equals(taskTodoType)) { //周期执行 + CronTriggerImpl cronTriggerImpl = new CronTriggerImpl(); + cronTriggerImpl.setCronExpression(task.getCronValue()); + // 创建一个Calendar对象,获取当前时间,并设置计划生成时段, + Calendar calendar = Calendar.getInstance(); + Date begindate = calendar.getTime(); //当前时间 + calendar.add(Calendar.DATE, 30); //第30天 + Date enddate = DateUtil.endOfDay(calendar.getTime()); + List dates = TriggerUtils.computeFireTimesBetween(cronTriggerImpl, null, begindate, enddate); + String fixedStartTime = task.getFixedStartTime(); + DateTime dateTime = DateUtil.date(); + if (StrUtil.isNotEmpty(fixedStartTime)) { + dateTime = DateUtil.parse(fixedStartTime); + } + +// ExecutorService executor = Executors.newFixedThreadPool(10); +// for (Date plandate : dates) { +// DateTime finalDateTime = dateTime; +// executor.submit(() -> { +// // 处理开始时间问题 +// if (finalDateTime.isAfter(plandate)) { +// return; +// } +// if (StrUtil.isNotEmpty(task.getInvalidStartTime()) && StrUtil.isNotEmpty(task.getInvalidEndTime())) { +// if (DateUtil.parse(task.getInvalidStartTime()).isBefore(plandate) && DateUtil.parse(task.getInvalidEndTime()).isAfter(plandate)) { +// return; +// } +// } +// setToDoTask(task, DateUtil.formatDateTime(plandate)); +// }); +// } +// // 关闭线程池并等待所有任务完成 +// executor.shutdown(); + + for (Date plandate : dates) { + // 处理开始时间问题 + if (dateTime.isAfter(plandate)) { + continue; + } + if (StrUtil.isNotEmpty(task.getInvalidStartTime()) && StrUtil.isNotEmpty(task + .getInvalidEndTime())) { + if (DateUtil.parse(task.getInvalidStartTime()).isBefore(plandate) && DateUtil.parse + (task.getInvalidEndTime()).isAfter(plandate)) { + continue; + } + } + setToDoTask(task, DateUtil.formatDateTime(plandate)); + } + } + return ""; + } + + @Override + public void createOnceTodoTask(String taskId) throws ParseException { + Task task = this.getById(taskId); + if (task == null) { + return; + } + if ("3".equals(task.getTaskTodoType())) { + //周期执行 + CronTriggerImpl cronTriggerImpl = new CronTriggerImpl(); + cronTriggerImpl.setCronExpression(task.getCronValue()); + // 创建一个Calendar对象,获取当前时间,并设置计划生成时段, + Calendar calendar = Calendar.getInstance(); + Date begindate = calendar.getTime(); //当前时间 + calendar.add(Calendar.DATE, 30); //第30天 + Date enddate = DateUtil.endOfDay(calendar.getTime()); + List dates = TriggerUtils.computeFireTimesBetween(cronTriggerImpl, null, begindate, enddate); + String fixedStartTime = task.getFixedStartTime(); + DateTime dateTime = DateUtil.date(); + if (StrUtil.isNotEmpty(fixedStartTime)) { + dateTime = DateUtil.parse(fixedStartTime); + } + for (Date plandate : dates) { + // 处理开始时间问题 + if (dateTime.isAfter(plandate)) { + continue; + } + if (StrUtil.isNotEmpty(task.getInvalidStartTime()) && StrUtil.isNotEmpty(task.getInvalidEndTime())) { + if (DateUtil.parse(task.getInvalidStartTime()).isBefore(plandate) && DateUtil.parse + (task.getInvalidEndTime()).isAfter(plandate)) { + continue; + } + } + String dateFormat = DateUtil.formatDateTime(plandate); + Integer integer = + taskTodoMapper.selectCount(new LambdaQueryWrapper().eq(TaskTodo::getPlanStartTime, dateFormat).eq(TaskTodo::getTaskId, task.getTaskId())); + if (integer > 0) { + continue; + } + setToDoTask(task, dateFormat); + break; + } + } + } + + /********************************** + * 用途说明: 根据任务创建对应一条代办任务清单, + * 参数说明 task 任务,planStartTime 计划开始时间 taskTodoType: + * 返回值说明: void + ***********************************/ + private String setToDoTask(Task task, String planStartTime) { + + TaskTodo taskTodo = new TaskTodo(); + String dockInfo = task.getDockInfo(); + + // ID + String taskTodoId = IdUtil.fastSimpleUUID(); + taskTodo.setTaskTodoId(taskTodoId); + if (StrUtil.isNotBlank(dockInfo) && JSONUtil.isTypeJSONObject(dockInfo)) { + com.alibaba.fastjson.JSONObject jsonObject = sendDockTask(task, planStartTime); + if (SystemCode.SUCCESS_STATUS_CODE.getCode().equals(jsonObject.getString("code"))) { + taskTodo.setTaskTodoId(jsonObject.getString("msg")); + } + } + // ID + taskTodo.setDockInfo(task.getDockInfo()); + // 任务id + taskTodo.setTaskId(task.getTaskId()); + // 机器人/无人机编码 + taskTodo.setRobotCode(task.getRobotCode()); + // 变电站代码 + taskTodo.setStationCode(task.getStationCode()); + // 巡视类型 + taskTodo.setType(task.getType()); + // 任务名称 + String taskName = task.getTaskName(); + taskTodo.setTaskName(taskName); + // 任务编码 + String taskCode = task.getTaskCode(); + taskTodo.setTaskCode(taskCode); + // 任务类型 + String taskType = task.getTaskType(); + taskTodo.setTaskType(taskType); + // 优先级 + String priority = task.getPriority(); + taskTodo.setPriority(priority); + // 任务状态:初始为0 待执行 + taskTodo.setTaskState("0"); + // 计划执行时间 + taskTodo.setPlanStartTime(planStartTime); + // 执行进度:初始为0 + taskTodo.setTaskProgress("0"); + // 监控索引号 + taskTodo.setControlNum(task.getControlNum()); + // 任务类别 1-摄像机任务 2-机器人任务 3-无人机 + taskTodo.setTaskClass(task.getTaskClass()); + // 机器人或者无人绑定的线路ID + taskTodo.setTaskLineid(task.getTaskLineid()); + // 机器人/无人机编码 + taskTodo.setRobotCode(task.getRobotCode()); + taskTodo.setDeviceFailureNum(0); + taskTodo.setDeviceDeforeNum(0); + taskTodo.setDeviceUnusualnum(0); + // 执行进度:剩余时间 + taskTodo.setTaskEstimatedTime("100"); + // 创建人 + taskTodo.setLastmodifier("定时创建"); + // 创建时间 + taskTodo.setLastmodifydate(new Timestamp(DateUtil.current())); + // 数据状态: 1 有效 + taskTodo.setDatastatus("1"); + String deviceList = task.getDeviceList(); + // String[] device = deviceList.split(","); + List idList = StrUtil.split(deviceList, ","); + // 绑定类型 + Integer deviceLevel = task.getDeviceLevel(); + List substationDevices = new ArrayList<>(); + switch (deviceLevel) { + case 1: + substationDevices.addAll(substationDeviceMapper.selectList(new LambdaQueryWrapper().in(SubstationDevice::getBayId, StrUtil.split(deviceList, ",")))); + break; + case 2: + substationDevices.addAll(substationDeviceMapper.selectList(new LambdaQueryWrapper().in(SubstationDevice::getMainDeviceId, StrUtil.split(deviceList, ",")))); + break; + case 3: + substationDevices.addAll(substationDeviceMapper.selectByIdList(deviceList)); + break; + case 4: + substationDevices.addAll(substationDeviceMapper.selectList(new LambdaQueryWrapper().in(SubstationDevice::getComponentId, StrUtil.split(deviceList, ",")))); + break; + default: + break; + } + // 巡视点位数量 + // 一键顺控和普通任务区分 + if ("4".equals(task.getTaskType())) { + LinkageSignal linkageSignal = + linkageSignalMapper.selectOne(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum + , task.getControlNum())); + if ("1".equals(linkageSignal.getLinkageType())) { + int min = Math.min(substationDevices.size(), 3); + taskTodo.setDeviceSumnum(min); +// taskTodo.setDeviceSumnum(1); + } else { + int sum = 0; + for (SubstationDevice substationDevice : substationDevices) { + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + sum += jsonArray.size(); + } + taskTodo.setDeviceSumnum(sum); + } + } else { + int sum = 0; + for (SubstationDevice substationDevice : substationDevices) { + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + sum += jsonArray.size(); + } + taskTodo.setDeviceSumnum(sum); + } + // int sum = 0; + // for (SubstationDevice substationDevice : substationDevices) { + // String patroldeviceJson = substationDevice.getPatroldeviceJson(); + // JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + // sum += jsonArray.size(); + // } + // taskTodo.setDeviceSumnum(sum); + // 插入待执行任务(单条) + taskTodoMapper.insert(taskTodo); + + int ordernum = 0; + List results = new ArrayList<>(); + for (SubstationDevice substationDevice : substationDevices) { + //修改为同一点位非同源任务 + String json = substationDevice.getPatroldeviceJson(); + JSONArray jsonArray = new JSONArray(); + if (JSONUtil.isTypeJSONObject(json)) { + //赋值到数组中 + jsonArray.add(JSONUtil.parseObj(json)); + } else if (JSONUtil.isTypeJSONArray(json)) { + jsonArray.addAll(JSONUtil.parseArray(json)); + } else { + //跳过 + continue; + } + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jobj = jsonArray.getJSONObject(i); + TaskResult taskResult = new TaskResult(); + String uuid = IdUtil.fastSimpleUUID(); + taskResult.setResultId(uuid); + taskResult.setTaskTodoId(taskTodo.getTaskTodoId()); + taskResult.setStationCode(taskTodo.getStationCode()); + // 机器人/无人机编码 + + taskResult.setTaskName(taskName); + taskResult.setTaskCode(taskCode); + ordernum++; + taskResult.setOrderNum(ordernum); + // 点位id + String deviceId = substationDevice.getDeviceId(); + taskResult.setDeviceId(deviceId); + // 点位名称 + String deviceName = substationDevice.getDeviceName(); + taskResult.setDeviceName(deviceName); + // 部件id + String componentId = substationDevice.getComponentId(); + taskResult.setComponentId(componentId); + // 部件名称 + String componentName = substationDevice.getComponentName(); + taskResult.setComponentName(componentName); + // 区域id + String areaId = substationDevice.getAreaId(); + taskResult.setAreaId(areaId); + // 区域名称 + String areaName = substationDevice.getAreaName(); + taskResult.setAreaName(areaName); + // 间隔id + String bayId = substationDevice.getBayId(); + taskResult.setBayId(bayId); + // 间隔名称 + String bayName = substationDevice.getBayName(); + taskResult.setBayName(bayName); + // 主设备id + String mainDeviceId = substationDevice.getMainDeviceId(); + taskResult.setMainDeviceId(mainDeviceId); + // 主设备名称 + String mainDeviceName = substationDevice.getMainDeviceName(); + taskResult.setMainDeviceName(mainDeviceName); + // 主设备名称 + String recognitionTypeList = substationDevice.getRecognitionTypeList(); + taskResult.setRecognitionType(recognitionTypeList); + // 单位 + String unit = substationDevice.getUnit(); + taskResult.setUnit(unit); + // 是否报警 + String isAlarm = substationDevice.getIsAlarm(); + taskResult.setIsAlarm(isAlarm); + + //数据来源 + String dataType = substationDevice.getDataType(); + taskResult.setDataType(dataType); + //摄像头 及预置位 + taskResult.setRobotCode(jobj.getStr("robot_code")); + //相机参数[{"patroldevice_name":"1","patroldevice_code":"","patroldevice_channelcode":"","patroldevice_pos":"","pos_name":"","baseimage":"","effective_region":[{"x1":"","y1":""},{"x2":"","y2":""}]}] + taskResult.setPatroldeviceName(jobj.getStr("patroldevice_name")); + taskResult.setPatroldeviceCode(jobj.getStr("patroldevice_code")); + taskResult.setPatroldeviceChannelcode(jobj.getStr("patroldevice_channelcode")); + taskResult.setPatroldevicePos(jobj.getStr("patroldevice_pos")); + taskResult.setPatroldeviceBaseimage(jobj.getStr("device_baseimage")); + taskResult.setPatroldeviceEffectiveregion(jobj.getStr("effective_region")); + //创建时间,创建者 + taskResult.setLastmodifier("定时创建"); + taskResult.setLastmodifydate(new Timestamp(DateUtil.current())); + //初始创建 + taskResult.setDatastatus("1"); + //未执行 + taskResult.setFlag("0"); + + //实物ID 待扩展 + taskResult.setMaterialId(""); + //文件类型 + taskResult.setFileType(substationDevice.getSaveTypeList()); + // 新增任务执行结果(初始值) + // taskResultMapper.insert(taskResult); + results.add(taskResult); + } + } + taskResultMapper.batchAdd(results); + //重点 QuartzTaskManage + String taskTodoType = task.getTaskTodoType(); + if ("2".equals(taskTodoType) || "3".equals(taskTodoType)) { + quartztaskManage.addJob(taskTodo); + } + return taskTodoId; + } + + /********************************** + * 用途说明: 删除大于当前时间的待执行计划列表 + * 参数说明 task + * 返回值说明: void + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteTodoTasks(Task task) { + taskTodoMapper.delete(new LambdaQueryWrapper().eq(TaskTodo::getTaskId, task.getTaskId()).isNull(TaskTodo::getStartTime).gt(TaskTodo::getPlanStartTime, DateUtil.now())); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskTodoServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskTodoServiceImpl.java new file mode 100644 index 0000000..e44f839 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TaskTodoServiceImpl.java @@ -0,0 +1,2808 @@ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.*; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.component.nettyclient.*; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.config.redis.RiisConstants; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationArea; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.SubstationAreaMapper; +import com.yfd.platform.modules.basedata.mapper.SubstationPatroldeviceMapper; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.modules.deviceapi.CameraUtil; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; +import com.yfd.platform.modules.patroltask.domain.TaskDolog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.ExaminePlanMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskDologMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.DockTaskService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.mapper.PlatformParentSystemMapper; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.*; +import io.netty.util.CharsetUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + *

+ * 巡视任务执行表 服务实现类 + *

+ * + * @since 2023-04-08 + */ +@Service +@Slf4j +public class TaskTodoServiceImpl extends ServiceImpl implements ITaskTodoService { + + + @Resource + private SubstationAreaMapper substationAreaMapper; + @Resource + SubstationPatroldeviceMapper patroldeviceMapper; + + @Resource + private ExaminePlanMapper examinePlanMapper; + + @Resource + private TaskTodoMapper taskTodoMapper; + + @Resource + private TaskResultMapper taskResultMapper; + + @Resource + private TaskDologMapper taskDologMapper; + + @Resource + private SubstationPatroldeviceMapper monitorDeviceMapper; + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + @Resource + private QuartzMultiTaskManage quartztaskManage; + + @Resource + private ISubstationDeviceService substationDeviceService; + @Resource + private ISubstationService substationService; + // @Resource + // private IStationNodeService stationNodeService; + + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; + + @Resource + private RedisTemplate redisTemplate; + @Resource + private HttpServerConfig httpServerConfig; + + @Resource + private ParentConfig parentConfig; + + @Resource + private HttpRESTfulUtils httpUtil; + @Resource + private TLSFTPUtils tlsftpUtils; + + @Resource + private PlatformParentSystemMapper platformParentSystemMapper; + + // @Resource + // private VideoService videoService; + + /********************************** + * 用途说明: 查询巡视任务执行情况详情 + * 参数说明 taskTodoId + * 返回值说明: java.util.Map + ***********************************/ + @Override + public Map getTaskToDo(String taskTodoId) { + + TaskTodo taskTodo = this.getById(taskTodoId); + // 将对象转换成map + Map map = BeanUtil.beanToMap(taskTodo); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskResult::getTaskTodoId, taskTodoId).select(TaskResult::getDeviceName, TaskResult::getAreaId, TaskResult::getAreaName, TaskResult::getMainDeviceName, TaskResult::getComponentName, TaskResult::getPatroldeviceName); + List> maps = taskResultMapper.selectMaps(queryWrapper); + List> resultMap = new ArrayList<>(); + for (Map strMap : maps) { + String areaId = strMap.get("areaId").toString(); + //String deviceId = strMap.get("deviceId").toString(); + SubstationArea substationArea = substationAreaMapper.selectById(areaId); + String stationName = substationArea.getStationName(); + strMap.put("stationName", stationName); + resultMap.add(strMap); + } + map.put("taskResult", resultMap); + return map; + } + + + /********************************** + * 用途说明: 分页查询巡视任务执行情况(前台) + * 参数说明 page + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getTaskImplementation(Page> page, String stationCode, + String currentDate, String startDate, String endDate, String taskState, String taskName) { + return taskTodoMapper.getTaskImplementation(page, stationCode, currentDate, startDate, endDate, taskState, taskName); + } + + + /********************************** + * 用途说明: 根据任务ID,处理待处理任务 + * 参数说明 taskTodoId 待处理任务ID optType操作类型 + * pause:暂停;resume:暂停后恢复 runNow:立即执行 delete: 删除 stop:终止(不可恢复); + * optType=delete 删除或取消 + * 返回值说明: 当前任务信息 + ***********************************/ + @Override + public TaskTodo doCurrentTask(String taskTodoId, String optType) throws InterruptedException { + //TaskState 0:待执行; 1:已执行;2:正在执行;3:暂停;4:终止;5:未执行;6:超期; + TaskTodo taskTodo = this.getById(taskTodoId); + String dotype = ""; + if (SystemCode.TASK_PAUSE_CODE.getCode().equals(optType)) { + + if ("0".equals(taskTodo.getTaskState())) { + // 待执行暂停 + quartztaskManage.pauseJob(taskTodo); + taskTodo.setCustom1("2"); + } else { + // 执行中暂停 + quartztaskManage.interruptJob(taskTodo); + // quartztaskManage.deleteJob(taskTodo); + taskTodo.setCustom1("3"); + } + + taskTodo.setCustom3("1"); + dotype = "手动暂停任务"; + taskTodo.setTaskState("3"); + taskTodoMapper.updateById(taskTodo); + if (!"0".equals(taskTodo.getTaskState()) && StrUtil.isBlank(taskTodo.getRobotCode())) { + resumeLowerTask(taskTodo.getStationCode(), taskTodoId, taskTodo.getPriority(), taskTodo.getCustom3()); + } + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), "msg"); + } + if (SystemCode.TASK_RESUME_CODE.getCode().equals(optType)) { + String custom1 = taskTodo.getCustom1(); + //暂停后恢复 + if ("2".equals(custom1)) { + taskTodo.setTaskState("0"); + quartztaskManage.resumeJob(taskTodo); + } else { + // quartztaskManage.addJob(taskTodo); + taskTodo.setTaskState("2"); + quartztaskManage.runJobNow(taskTodo); + } + + // 汤伟修改的逻辑 + taskTodo.setCustom1(""); + dotype = "手动恢复任务"; + taskTodoMapper.updateById(taskTodo); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), "msg"); + } + if (SystemCode.TASK_RUN_CODE.getCode().equals(optType)) { + //立即执行 + List list = taskTodoMapper.selectList(new LambdaQueryWrapper(). + eq(TaskTodo::getStationCode, taskTodo.getStationCode()). + in(TaskTodo::getTaskState, "2"). + ne(TaskTodo::getTaskTodoId, taskTodo.getTaskTodoId()).orderByDesc(TaskTodo::getPlanStartTime) + ); + for (TaskTodo taskTodo1 : list) { + quartztaskManage.interruptJob(taskTodo1); //中断 + // 汤伟加的逻辑 + taskTodo1.setTaskState("3"); + taskTodo1.setCustom3(""); + taskTodo1.setCustom1("3"); //程序控制的中断执行 + taskTodoMapper.updateById(taskTodo1); + } + taskTodo.setTaskState("2"); + taskTodo.setCustom3("2"); + taskTodo.setStartTime(DateUtil.date().toString()); + taskTodoMapper.updateById(taskTodo); + quartztaskManage.runJobNow(taskTodo); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), "msg"); + } + if (SystemCode.TASK_DELETE_CODE.getCode().equals(optType) || SystemCode.TASK_STOP_CODE.getCode().equals(optType)) {//删除 或者 终止 + quartztaskManage.deleteJob(taskTodo); + taskTodo.setTaskState("4"); + dotype = "手动终止任务"; + taskTodoMapper.updateById(taskTodo); + } + // 记录任务操作日志 + TaskDolog taskDolog = new TaskDolog(); + taskDolog.setId(IdUtil.fastSimpleUUID()); + taskDolog.setStationCode(taskTodo.getStationCode()); + taskDolog.setTaskCode(taskTodo.getTaskCode()); + taskDolog.setTaskName(taskTodo.getTaskName()); + taskDolog.setTaskTodoid(taskTodo.getTaskTodoId()); + taskDolog.setPlanStartTime(taskTodo.getPlanStartTime()); + taskDolog.setDoType(dotype); + String logger = ""; + logger = SecurityUtils.getCurrentUsername(); + taskDolog.setLogger(logger); + taskDolog.setLogTime(new Timestamp(DateUtil.current())); + taskDolog.setDescription(String.format("%s在%s,对%s[%s],执行了 [%s] 操作!", logger, DateUtil.now(), + taskTodo.getTaskName(), taskTodo.getTaskTodoId(), dotype)); + recordTaskDoLog(taskDolog); + return taskTodo; + } + + /********************************** + * 用途说明: 根据任务ID,查询巡视点位清单 + * 参数说明 taskTodoId 待处理任务ID + * 返回值说明: 任务对象列表 + * + ***********************************/ + @Override + public List> queryTaskResult(String taskTodoId) { + return taskTodoMapper.queryTaskResult(taskTodoId); + } + + @Override + //巡视点位 时间是否与检修计划时间冲突 + public int queryExaminePlan(String deviceid, String planStartTime) { + int count = examinePlanMapper.selectCount(new LambdaQueryWrapper(). + eq(ExaminePlan::getDatastatus, "1"). + like(ExaminePlan::getDeviceList, deviceid). + lt(ExaminePlan::getStartTime, planStartTime). + gt(ExaminePlan::getEndTime, planStartTime)); + return count; + } + + @Override + //供智能识别程序调用,更新识别结果 + @Transactional(rollbackFor = Exception.class) + public boolean updateTaskResult(TaskResult taskResult) { + String todotaskid = taskResult.getTaskTodoId(); + String valid = taskResult.getValid(); + if (valid.equals("0")) {//识别识别 + taskResult.setFlag("4"); //4:巡视失败 + } else if (valid.equals("1")) {//识别成功 + taskResult.setFlag("2"); + } + taskResult.setTime(DateUtil.now()); + taskResult.setLastmodifydate(new Timestamp(DateUtil.current())); + taskResultMapper.updateById(taskResult); + + //更新巡视任务 + TaskTodo taskTodo = taskTodoMapper.selectById(todotaskid); + int sumnum = taskTodo.getDeviceSumnum(); + int percent = 0; + //已分析完成数量 + int anysislnum = taskResultMapper.selectCount( + new LambdaQueryWrapper(). + eq(TaskResult::getTaskTodoId, todotaskid). + ne(TaskResult::getFlag, "0") + ); + // 赋值执行数量 + List> list = this.getTaskResultById(taskTodo.getTaskTodoId(), null, null, null); + // 已送检点数 + long patrolCount = list.stream().filter(t -> !("0").equals(t.get("flag")) && !("1").equals(t.get("flag"))).count(); + // 失败的数量 + long failCount = + list.stream().filter(t -> ("4").equals(t.get("flag")) || ("6").equals(t.get("flag"))).count(); + // 异常数量 + long abnormalCount = list.stream().filter(t -> ("2").equals(t.get("valid1"))).count(); + taskTodo.setDeviceDeforeNum((int) patrolCount); + taskTodo.setDeviceUnusualnum((int) abnormalCount); + taskTodo.setDeviceFailureNum((int) failCount); + //检修跳过数量 + // int checklnum = taskResultMapper.selectCount( + // new LambdaQueryWrapper(). + // eq(TaskResult::getTaskTodoId, todotaskid). + // eq(TaskResult::getFlag, "3") + // ); + // int totalnum = sumnum - checklnum; + int totalnum = sumnum; + if (totalnum > 0) { + //百分之多少 + percent = anysislnum * 100 / totalnum; + taskTodo.setEndTime(DateUtil.now()); + } + if (!"100".equals(taskTodo.getTaskProgress())) { + taskTodo.setTaskProgress(String.valueOf(percent)); + } +// taskTodo.setTaskProgress(String.valueOf(percent)); + taskTodo.setDeviceNow(taskResult.getDeviceId()); + taskTodo.setLastmodifydate(new Timestamp(DateUtil.current())); + if (percent == 100) { + taskTodo.setTaskState("1");//任务已执行 + taskTodo.setTaskEstimatedTime("0"); + taskTodo.setEndTime(DateUtil.now()); + } + taskTodoMapper.updateById(taskTodo); + return true; + } + + @Override + //更新巡视任务状态和巡视任务执行进度 + //flag:'0未巡视,1:已巡视(图像已采集),2:已完成(图像已分析);3:设备检修中;4:巡视失败;5:已修正 6:已巡视(图像采集失败)' +// @Transactional(rollbackFor = Exception.class) + public synchronized boolean updateTaskResultStatus(String todotaskid, String taskresultid, String imagefilename, String flag) { + //更新当前任务巡检点巡视完成状态 + TaskResult taskResult = taskResultMapper.selectById(taskresultid); + taskResult.setResultId(taskresultid); + taskResult.setFilePath(imagefilename); + taskResult.setDefectFilePath(imagefilename); //检测图赋值和初始图一致 + taskResult.setFlag(flag); + String now = DateUtil.now(); + taskResult.setPatroldeviceDate(now); + if ("1".equals(flag)) { + taskResult.setTime(now); + } + taskResult.setLastmodifydate(new Timestamp(DateUtil.current())); + taskResultMapper.updateById(taskResult); + + //更新当前任务状态及进度信息 + TaskTodo taskTodo = taskTodoMapper.selectById(todotaskid); + int sumnum = taskTodo.getDeviceSumnum(); + int percent = 0; + //巡视数量 + int patrolnum = + taskResultMapper.selectCount(new LambdaQueryWrapper().eq(TaskResult::getTaskTodoId, + todotaskid).ne(TaskResult::getFlag, "0")); + + // 赋值执行数量 + List> list = this.getTaskResultById(taskTodo.getTaskTodoId(), null, null, null); + // 已送检点数 + long patrolCount = list.stream().filter(t -> !("0").equals(t.get("flag")) && !("1").equals(t.get("flag"))).count(); + // 失败的数量 + long failCount = + list.stream().filter(t -> ("4").equals(t.get("flag")) || ("6").equals(t.get("flag"))).count(); + // 异常数量 + long abnormalCount = list.stream().filter(t -> ("2").equals(t.get("valid1"))).count(); + taskTodo.setDeviceDeforeNum((int) patrolCount); + taskTodo.setDeviceUnusualnum((int) abnormalCount); + taskTodo.setDeviceFailureNum((int) failCount); + + percent = patrolnum * 100 / sumnum; //百分之多少 + if (!"100".equals(taskTodo.getTaskProgress())) { + taskTodo.setTaskProgress(String.valueOf(percent)); + } +// taskTodo.setTaskProgress(String.valueOf(percent)); + boolean b = patrolnum == sumnum; + if (b) { + taskTodo.setTaskState("1"); + taskTodo.setEndTime(DateUtil.now()); + } + taskTodo.setDeviceNow(taskResult.getDeviceId());//当前巡视点 + taskTodo.setLastmodifydate(new Timestamp(DateUtil.current())); + taskTodoMapper.updateById(taskTodo); + // 向上级发送任务数据 +// this.sendTaskResult(taskTodo, taskResult); + String stationCode = taskResult.getStationCode(); + JSONObject jsonObject = new JSONObject(); + String taskTodoId = taskTodo.getTaskTodoId(); + String taskType = taskTodo.getTaskType(); + jsonObject.putOnce("taskTodoId", taskTodoId); + jsonObject.putOnce("taskType", taskType); + WebSocketServer.sendInfo("taskresult_" + stationCode, jsonObject.toString()); + + + // 巡视任务状态发生变化或任务进度发生变化时,区域巡视主机主动向上级系统上报状态数据 + return true; + } + + /********************************** + * 用途说明: 上传任务执行信息到上级 + * 参数说明 taskTodo + * 参数说明 taskResult + * 返回值说明: void + ***********************************/ + @Override + public void sendTaskResult(TaskTodo taskTodo, TaskResult taskResult) { + String filePath = taskResult.getFilePath(); +// if(StrUtil.isNotBlank(taskResult.getDefectFilePath())){ +// filePath=taskResult.getDefectFilePath(); +// } + boolean connect = false; + try { + List list = platformParentSystemMapper.selectList(new LambdaQueryWrapper().eq(PlatformParentSystem::getIsEnable, "1")); + if (list.size() > 0) { + PlatformParentSystem platformParentSystem = list.get(0); + if (StrUtil.isNotBlank(filePath)) { + if (tlsftpUtils.isConnected()) { + tlsftpUtils.disconnect(); + } + filePath = URLDecoder.decode(filePath, "utf-8"); + String suff = "/home/ftpsuser"; + connect = tlsftpUtils.connect(platformParentSystem.getFtpIp(), platformParentSystem.getFtpUser(), platformParentSystem.getFtpPassword(), NumberUtil.parseInt(platformParentSystem.getFtpPort())); + String path = StrUtil.subBefore(File.separator + filePath, "/", true); + tlsftpUtils.upload(suff + path, new File(httpServerConfig.getSnapFilePath() + filePath)); + } + } + String patroldeviceCode = taskResult.getPatroldeviceCode(); + String patrolDeviceCode = patroldeviceCode; + List substationPatroldevices = patroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getInternationalId, patroldeviceCode)); + if (substationPatroldevices.size() > 0) { + SubstationPatroldevice substationPatroldevice = substationPatroldevices.get(0); + patrolDeviceCode = substationPatroldevice.getPatroldeviceCode(); + } + JSONObject taskResultData = new JSONObject(); + taskResultData.putOpt("patroldevice_name", taskResult.getPatroldeviceName()); + taskResultData.putOpt("patroldevice_code", patrolDeviceCode); + taskResultData.putOpt("task_name", taskResult.getTaskName()); + taskResultData.putOpt("task_code", taskResult.getTaskCode()); + taskResultData.putOpt("device_name", taskResult.getDeviceName()); + taskResultData.putOpt("device_id", taskResult.getDeviceId()); + taskResultData.putOpt("value_type", taskResult.getValueType()); + taskResultData.putOpt("value", taskResult.getValue()); + String unit = ""; + if (StrUtil.isNotBlank(taskResult.getUnit())) { + unit = taskResult.getUnit(); + } + String value = taskResult.getValue(); + if (!("1".equals(taskResult.getRecognitionType()) || "4".equals(taskResult.getRecognitionType()))) { + value = taskResult.getDesc(); + } + taskResultData.putOpt("value_unit", value + unit); + taskResultData.putOpt("unit", taskResult.getUnit()); + taskResultData.putOpt("time", taskResult.getTime()); + taskResultData.putOpt("recognition_type", taskResult.getRecognitionType()); + taskResultData.putOpt("file_type", taskResult.getFileType()); + taskResultData.putOpt("file_path", filePath); + String rectangle = taskResult.getRectangle(); +// if(StrUtil.isNotBlank(rectangle)){ +// JSONArray jsonArray = JSONUtil.parseArray(rectangle); +// +// } + taskResultData.putOpt("rectangle", ""); + taskResultData.putOpt("task_patrolled_id", taskResult.getTaskTodoId()); + taskResultData.putOpt("valid", taskResult.getValid()); + taskResultData.putOpt("flag", taskResult.getFlag()); + this.sendTaskData(SystemCode.TYPE_TASK_RESULT_CODE.getCode(), "", taskTodo.getStationCode(), taskResultData.toString()); + tlsftpUtils.disconnect(); + } catch (Exception e) { + if (connect) { + tlsftpUtils.disconnect(); + } + log.error(e.getMessage());; + + } + JSONObject taskData = new JSONObject(); + taskData.putOpt("task_patrolled_id", taskTodo.getTaskTodoId()); + taskData.putOpt("task_name", taskTodo.getTaskName()); + taskData.putOpt("task_code", taskTodo.getTaskCode()); + taskData.putOpt("task_state", taskTodo.getTaskState()); + taskData.putOpt("plan_start_time", taskTodo.getPlanStartTime()); + taskData.putOpt("start_time", taskTodo.getStartTime()); + taskData.putOpt("task_progress", taskTodo.getTaskProgress()); + taskData.putOpt("task_estimated_time", taskTodo.getTaskEstimatedTime()); + taskData.putOpt("description", taskTodo.getDescription()); + this.sendTaskData(SystemCode.TYPE_TASK_STATUS_CODE.getCode(), "", taskTodo.getStationCode(), taskData.toString()); + } + + public void sendTaskData(String Type, String Command, String Code, String Items) { + String xml = MyXmlUtil.getXml(httpServerConfig.getPatrolServerid(), parentConfig.getParentId(), Type + , Command, Code, Items); + log.info(xml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getRequestSessionID()); + messageProtocol.setReceiverSerialNo(new Long(0L)); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if (BootNettyClientChannelCache.channelMapCache.size() > 0) { + for (Map.Entry entry : + BootNettyClientChannelCache.channelMapCache.entrySet()) { + BootNettyClientChannel bootNettyChannel = entry.getValue(); + if (bootNettyChannel != null && bootNettyChannel.getChannel().isOpen()) { + bootNettyChannel.getChannel().writeAndFlush(messageProtocol); + } +// else { +// BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), +// Integer.parseInt(parentConfig.getTcpPort()), parentConfig); +// thread.start(); +// } + } + } +// else { +// BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), +// Integer.parseInt(parentConfig.getTcpPort()), parentConfig); +// thread.start(); +// } + } + + @Override + public boolean pauseLowerTask(String stationcode, String todotaskid, String priority) { + List list = taskTodoMapper.selectList(new LambdaQueryWrapper(). + eq(TaskTodo::getStationCode, stationcode). + in(TaskTodo::getTaskState, "2"). + //优先级低于当前级别任务 + lt(TaskTodo::getPriority, priority). + ne(TaskTodo::getTaskTodoId, todotaskid).orderByDesc(TaskTodo::getPlanStartTime) + ); + for (TaskTodo taskTodo : list) { + if ("2".equals(taskTodo.getTaskState())) { + //执行中 + //中断 + quartztaskManage.interruptJob(taskTodo); + // 汤伟加的逻辑 + taskTodo.setTaskState("3"); + //程序控制的中断执行 + taskTodo.setCustom1("3"); + taskTodoMapper.updateById(taskTodo); + } + } + return true; + } + + /********************************** + * 用途说明: 任务开始前,判断是否有任务等级大于等于当前任务的,有就暂停当前任务 + * 参数说明 stationCode + * 参数说明 taskTodoId + * 参数说明 priority + * 返回值说明: boolean + ***********************************/ + @Override + public boolean pauseUpperTask(String stationCode, String taskTodoId, String priority) { + Integer count = taskTodoMapper.selectCount(new LambdaQueryWrapper(). + isNull(TaskTodo::getRobotCode). + eq(TaskTodo::getStationCode, stationCode). + eq(TaskTodo::getTaskState, "2"). + //between(TaskTodo::getPlanStartTime,DateUtil.now(),DateUtil.format(enddate,"yyyy-MM-dd HH:mm:ss")). + ge(TaskTodo::getPriority, priority).ne(TaskTodo::getTaskTodoId, taskTodoId)//优先级大于等于当前级别 + ); + TaskTodo taskTodo = taskTodoMapper.selectById(taskTodoId); + Integer count1 = taskTodoMapper.selectCount(new LambdaQueryWrapper(). + isNull(TaskTodo::getRobotCode). + eq(TaskTodo::getStationCode, stationCode). + eq(TaskTodo::getTaskState, "0"). + eq(TaskTodo::getPlanStartTime, taskTodo.getPlanStartTime()). + //between(TaskTodo::getPlanStartTime,DateUtil.now(),DateUtil.format(enddate,"yyyy-MM-dd HH:mm:ss")). + gt(TaskTodo::getPriority, priority).ne(TaskTodo::getTaskTodoId, taskTodoId)//优先级大于等于当前级别 + ); + if (count > 0 || count1 > 0) { + /*if ("0".equals(taskTodo.getTaskState())) {//待执行 + quartztaskManage.pauseJob(taskTodo); + taskTodo.setTaskState("3"); + taskTodo.setCustom1("2"); //程序控制的暂停 + taskTodoMapper.updateById(taskTodo); + }*/ + if ("2".equals(taskTodo.getTaskState())) {//执行中 + quartztaskManage.interruptJob(taskTodo); //中断 + taskTodo.setTaskState("3"); + taskTodo.setCustom1("3"); //程序控制的中断执行 + taskTodoMapper.updateById(taskTodo); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), "msg"); + } + } + return true; + } + + @Override + public boolean resumeLowerTask(String stationcode, String todotaskid, String priority, String custom3) throws InterruptedException { + DateTime date = DateUtil.date(); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(date); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskTodo::getStationCode, stationcode). + eq(TaskTodo::getTaskState, "3"). + ne(TaskTodo::getTaskTodoId, todotaskid). + //程序控制(暂停了的或者中断执行的) + in(TaskTodo::getCustom1, "3"). + //优先级低于等于当前级别任务 + //lt(TaskTodo::getPriority, priority)//优先级低于当前级别任务 + le(TaskTodo::getPriority, priority).ge(TaskTodo::getPlanStartTime, beginOfDay).le(TaskTodo::getPlanStartTime, DateUtil.date()); + if (StrUtil.isBlank(custom3) || "2".equals(custom3)) { + queryWrapper.and(q -> q.isNull(TaskTodo::getCustom3).or().eq(TaskTodo::getCustom3, "")); + } + queryWrapper.orderByAsc(TaskTodo::getPlanStartTime).orderByDesc(TaskTodo::getPriority); + List list = taskTodoMapper.selectList(queryWrapper); + if (list != null && list.size() > 0) { + TaskTodo taskTodo = list.get(0); + taskTodo.setTaskState("2"); + taskTodo.setCustom1(""); + taskTodoMapper.updateById(taskTodo); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), "msg"); + //如果是手动暂停就恢复,如果是系统暂停立即执行一次 + quartztaskManage.runJobNow(taskTodo); + } + return true; + } + + @Override + public boolean recordTaskDoLog(TaskDolog taskDolog) { + taskDologMapper.insert(taskDolog); + return true; + } + + @Override + public List> getTaskToDoStatList(String stationCode, String currentDate, String startDate, + String endDate, String taskName) { + return taskTodoMapper.getTaskToDoStatList(stationCode, currentDate, startDate, endDate, taskName); + } + + @Override + public Page> getTaskReportList(Page> page, String stationCode, + String areaId, + String taskName, String type, String startDate, String endDate + , String bayId, String patrolType) { + return taskTodoMapper.getTaskReportList(page, stationCode, areaId, taskName, type, startDate, endDate, bayId, + patrolType); + } + + /********************************** + * 用途说明: 根据任务执行情况查询任务结果 + * 参数说明 taskTodoId 任务执行情况id + * 返回值说明: java.util.List + ***********************************/ + @Override + public List> getTaskResultById(String taskTodoId, String areaId, String bayId, String mainDeviceId) { + return taskResultMapper.getTaskResultById(taskTodoId, areaId, bayId, mainDeviceId); + } + + /********************************** + * 用途说明: 异常点位查询 + * 参数说明 page + * 参数说明 taskTodoIds + * 参数说明 taskName + * 参数说明 deviceName + * 参数说明 patroldeviceName + * 参数说明 componentName + * 参数说明 recognitionType + * 参数说明 status + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getAbnormalDeviceList(Page> page, String stationCode, + String areaId, + String bayId, + String taskName, + String deviceName, String patroldeviceName, + String componentName, String recognitionType, String status, + String startDate, String endDate) { + return taskResultMapper.getAbnormalDeviceList(page, stationCode, areaId, bayId, taskName, + deviceName, patroldeviceName, componentName, recognitionType, status, startDate, endDate); + } + + /********************************** + * 用途说明: 导出异常点位 + * 参数说明 records + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void exportAbnormalDevice(List> records, HttpServletResponse response) throws IOException { + List> recognition_type = sysDictionaryItemsService.getDeviceByType("recognition_type"); + List> DeviceClass = sysDictionaryItemsService.getDeviceByType("DeviceClass"); + for (Map record : records) { + String deviceClass = ""; + if (!Objects.isNull(record.get("deviceClass"))) { + Map map = + DeviceClass.stream().filter(r -> !Objects.isNull(record.get("deviceClass")) && r.get( + "itemcode").equals(record.get("deviceClass").toString())).findFirst().orElse(null); + if (map != null) { + deviceClass = map.get("dictname").toString(); + } + } + record.put("deviceClassName", deviceClass); + String recognitionType = ""; + if (!Objects.isNull(record.get("recognitionType"))) { + Map map = recognition_type.stream().filter(r -> !Objects.isNull(record.get( + "recognitionType")) && r.get("itemcode").equals(record.get("recognitionType").toString())).findFirst().orElse(null); + if (map != null) { + recognitionType = map.get("dictname").toString(); + } + } + record.put("recognitionTypeName", recognitionType); + if (ObjectUtil.isNotEmpty(record.get("defectFilePath"))) { + String filePath = record.get("defectFilePath").toString(); + filePath = URLDecoder.decode(filePath, "utf-8"); + //filePath = filePath.replace("\\", "/"); + // hashMap.put("巡视图片", new FileInputStream(new File("D:\\riis\\video\\" + filePath))); + record.put("img", httpServerConfig.getSnapFilePath() + filePath); + + } + if (ObjectUtil.isNotEmpty(record.get("value"))) { + String value = record.get("value").toString(); + String unit = ObjectUtil.isNotEmpty(record.get("unit")) ? record.get("unit").toString() : ""; + record.put("value", value + unit); + } + } + String[] headers = {"区域名称", "间隔名称", "设备名称", "部件名称", "点位名称", "点位分类", "识别类型", "巡视结果", "巡视结论", "巡视时间", "巡视图片", + "审核人", + "审核时间"}; + String[] keys = {"areaName", "bayName", "taskName", "patroldeviceName", "componentName", "deviceName", + "deviceClassName", + "recognitionTypeName", "value", "analysisResult", "time", "img", "cexamineUserName", "examineDate"}; + String imgs = "img"; + FileUtil.excelImg(records, headers, keys, imgs, "", response); + } + + /********************************** + * 用途说明: 根据任务执行情况ID查询点位查询 + * 参数说明 page + * 参数说明 taskTodoId + * 参数说明 valid + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getTaskDeviceList(Page> page, String taskTodoId, + String deviceName, String valid) { + return taskResultMapper.getTaskDeviceList(page, taskTodoId, deviceName, valid); + } + + /********************************** + * 用途说明: 查询点位历史曲线 + * 参数说明 deviceId + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getHistoricalCurve(String deviceId) { + return taskResultMapper.getHistoricalCurve(deviceId); + } + + /********************************** + * 用途说明: 根据巡视任务执行情况ID查看详情 + * 参数说明 taskTodoId + * 返回值说明: java.util.Map + ***********************************/ + @Override + public Map getTaskTodoById(String taskTodoId, String areaId, String bayId, String mainDeviceId) { + TaskTodo taskTodo = this.getById(taskTodoId); + if (taskTodo == null) { + return null; + } + long deviceSumnum = taskTodo.getDeviceSumnum(); + Map map = BeanUtil.beanToMap(taskTodo); + String stationCode = taskTodo.getStationCode(); + Substation substation = + substationService.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, + stationCode)); + map.put("isStationFlag", substation.getIsStationFlag()); + List> list = this.getTaskResultById(taskTodo.getTaskTodoId(), areaId, bayId, mainDeviceId); + // 已巡视 + long patrolCount = list.stream().filter(t -> !("0").equals(t.get("flag"))).count(); + map.put("patrolCount", patrolCount); + // 检修数量 + long serviceCount = list.stream().filter(t -> ("3").equals(t.get("flag"))).count(); + map.put("serviceCount", serviceCount); + + // 成功的数量 + long successCount = + list.stream().filter(t -> ("2").equals(t.get("flag")) || ("5").equals(t.get("flag"))).count(); + map.put("successCount", successCount); + // 失败的数量 + long failCount = + list.stream().filter(t -> ("4").equals(t.get("flag")) || ("6").equals(t.get("flag"))).count(); + map.put("failCount", failCount); + long normalCount = list.stream().filter(t -> ("1").equals(t.get("valid1"))).count(); + long abnormalCount = list.stream().filter(t -> ("2").equals(t.get("valid1"))).count(); + // 正常数量 + map.put("normalCount", normalCount); + // 异常数量 + map.put("abnormalCount", abnormalCount); + long count = deviceSumnum - patrolCount <= 0 ? 0 : deviceSumnum - patrolCount; + String custom1 = "总点位" + deviceSumnum + "个,已检点位" + patrolCount + "个,未检点位" + count + + "个,正常点位" + normalCount + "个,异常点位" + abnormalCount + "个"; + String detail = "成功" + successCount + ";失败" + failCount + ";正常" + normalCount + ";异常" + abnormalCount; + map.put("custom1", custom1); + map.put("detail", detail); + return map; + } + + /********************************** + * 用途说明: 查询主设备所关联的点位 + * 参数说明 ids + * 参数说明 meterType + * 参数说明 recognitionType + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getDeviceByMainDevice(String mainDeviceId, String meterType, String recognitionType + , String startDate, String endDate) { + return taskTodoMapper.getDeviceByMainDevice(mainDeviceId, meterType, recognitionType, startDate, endDate); + } + + /********************************** + * 用途说明: 修正巡视结果 + * 参数说明 taskResult + * 返回值说明: boolean + ***********************************/ + @Override + public boolean updateTaskResultById(TaskResult taskResult) { + int i = taskResultMapper.updateById(taskResult); + return i > 0; + } + + /********************************** + * 用途说明: 数据对比分析列表 + * 参数说明 page + * 参数说明 deviceId + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getTaskResultList(Page> page, String deviceId, + String startDate, String endDate) { + String[] split = deviceId.split(","); + List idList = Arrays.asList(split); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TaskResult::getDeviceId, idList).eq(TaskResult::getDatastatus, "1"); + // 开始时间判断 + if (StrUtil.isNotBlank(startDate)) { + queryWrapper.le(TaskResult::getTime, endDate); + } + // 结束时间判断 + if (StrUtil.isNotBlank(endDate)) { + queryWrapper.ge(TaskResult::getTime, startDate); + } + queryWrapper.orderByAsc(TaskResult::getTime); + return taskResultMapper.selectMapsPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 数据对比分析曲线 + * 参数说明 deviceId + * 返回值说明: java.util.List> + ***********************************/ + @Override + public Map getAnalysisCurve(String deviceId, String startDate, String endDate) { + String[] split = deviceId.split(","); + String[] ids = split; + List dayList = getDays(startDate, endDate); + List dateList = new ArrayList<>(); + for (String day : dayList) { + int i = day.indexOf("-"); + dateList.add(day.substring(i + 1)); + } + Map deviceMap = new HashMap<>(); + deviceMap.put("date", dateList); + List> maps = new ArrayList<>(); + long l = System.currentTimeMillis(); + for (String id : ids) { + Map map = new HashMap<>(); + SubstationDevice substationDevice = substationDeviceService.getById(id); + map.put("id", id); + map.put("name", substationDevice.getDeviceName()); + List values = new ArrayList<>(dateList.size()); + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskResult::getDatastatus, "1").le(TaskResult::getTime, endDate).ge(TaskResult::getTime, + startDate).eq(TaskResult::getDeviceId, id).select(TaskResult::getValue, TaskResult::getUnit, TaskResult::getTime).orderByDesc(TaskResult::getTime); + List taskResultList = taskResultMapper.selectList(queryWrapper); + for (String date : dayList) { + List list = taskResultList.stream().filter(t -> t.getTime().contains(date)).collect(Collectors.toList()); + if (list.size() > 0) { + TaskResult taskResult = list.get(0); + values.add(taskResult.getValue()); + if (!map.containsKey("unit")) { + map.put("unit", taskResult.getUnit()); + } + } else { + values.add(null); + } + + } + map.put("values", values); + maps.add(map); + } + deviceMap.put("device", maps); + return deviceMap; + } + + + /********************************** + * 用途说明: 导出数据对比分析列表 + * 参数说明 records + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void exportTaskResultList(List> records, String exportFields, + HttpServletResponse response) throws IOException { + List> recognition_type = sysDictionaryItemsService.getDeviceByType("recognition_type"); + List> types = sysDictionaryItemsService.getDeviceByType("PatrolEquipmentType"); + for (Map record : records) { + String recognitionType = ""; + if (!Objects.isNull(record.get("recognitionType"))) { + Map map = recognition_type.stream().filter(r -> r.get("itemcode").equals(record.get( + "recognitionType").toString())).findFirst().orElse(null); + if (map != null) { + recognitionType = map.get("dictname").toString(); + } + } + + String type = ""; + if (!Objects.isNull(record.get("type"))) { + Map map = types.stream().filter(r -> r.get("itemcode").equals(record.get( + "type").toString())).findFirst().orElse(null); + if (map != null) { + type = map.get("dictname").toString(); + } + } + record.put("type", type); + record.put("recognitionTypeName", recognitionType); + if (ObjectUtil.isNotEmpty(record.get("defectFilePath"))) { + String filePath = record.get("defectFilePath").toString(); + filePath = URLDecoder.decode(filePath, "utf-8"); + //filePath = filePath.replace("\\", "/"); + // hashMap.put("巡视图片", new FileInputStream(new File("D:\\riis\\video\\" + filePath))); + record.put("img", httpServerConfig.getSnapFilePath() + filePath); + + } else { + String filePath = ObjectUtil.isNotEmpty(record.get("filePath")) ? + record.get("filePath").toString() : ""; + filePath = URLDecoder.decode(filePath, "utf-8"); + record.put("img", httpServerConfig.getSnapFilePath() + filePath); + } + if (ObjectUtil.isNotEmpty(record.get("value"))) { + String value = record.get("value").toString(); + String unit = ObjectUtil.isNotEmpty(record.get("unit")) ? record.get("unit").toString() : ""; + record.put("value", value + unit); + } + } + // String[] headers = {"区域名称", "间隔名称", "设备名称", "部件名称", "巡视点位", "巡视结果", "识别类型", "巡视结论", "巡视时间", "巡视图片"}; + // String[] keys = {"areaName", "bayName", "patroldeviceName", "componentName", "deviceName", "value", + // "recognitionTypeName", "analysisResult", "time", "img"}; + String[] headers = new String[20]; + String[] keys = new String[20]; + String[] fields = exportFields.split(","); + String imgs = ""; + for (int i = 0; i < fields.length; i++) { + String[] name = fields[i].split("-"); + keys[i] = name[0]; + if ("img".equals(name[0])) { + imgs = "img"; + } + headers[i] = name[1]; + + } + FileUtil.excelImg(records, headers, keys, imgs, "", response); + } + + /********************************** + * 用途说明: 导出异常点位(导出全部) + * 参数说明 page + * 参数说明 taskTodoId + * 参数说明 valid + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public List> getAbnormalDevice(String stationCode, String taskName, + String deviceName, String patroldeviceName, + String componentName, String recognitionType, String status, + String startDate, String endDate) { + + List> records = taskResultMapper.getAbnormalDevice(stationCode, taskName, deviceName, + patroldeviceName, componentName, recognitionType, status, startDate, endDate); + records.forEach(m -> { + String deviceId = m.get("deviceId").toString(); + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + m.put("deviceClass", substationDevice.getDeviceClass()); + String flag = ""; + if (ObjectUtil.isNotEmpty(m.get("flag"))) { + flag = m.get("flag").toString(); + } + String valid = ""; + if (ObjectUtil.isNotEmpty(m.get("valid1"))) { + valid = m.get("valid1").toString(); + } + if ("4".equals(flag)) { + m.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + m.put("analysisResult", "采集失败"); + } else if ("2".equals(valid)) { + m.put("analysisResult", "异常"); + } + }); + return records; + } + + /********************************** + * 用途说明: 数据对比分析列表(查询符合条件的所有) + * 参数说明 page + * 参数说明 deviceId + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public List> getTaskResultAll(String deviceId, String startDate, String endDate) { + + String[] split = deviceId.split(","); + List idList = Arrays.asList(split); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(TaskResult::getDeviceId, idList).eq(TaskResult::getDatastatus, "1"); + // 开始时间判断 + if (StrUtil.isNotBlank(startDate)) { + queryWrapper.le(TaskResult::getTime, endDate); + } + // 结束时间判断 + if (StrUtil.isNotBlank(endDate)) { + queryWrapper.ge(TaskResult::getTime, startDate); + } + queryWrapper.orderByDesc(TaskResult::getTime); + return taskResultMapper.selectMaps(queryWrapper); + } + + /********************************** + * 用途说明: 获取当前时间最近的数据行 + * 参数说明 + * 返回值说明: long + ***********************************/ + @Override + public long getLastTimePosition(String stationCode, String taskName, String startDate, String endDate, String taskState) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(TaskTodo::getStationCode, stationCode); + if (StrUtil.isNotBlank(taskName)) { + queryWrapper.like(TaskTodo::getTaskName, taskName); + } + if (StrUtil.isNotBlank(startDate)) { + queryWrapper.le(TaskTodo::getPlanStartTime, endDate); + } + if (StrUtil.isNotBlank(endDate)) { + queryWrapper.ge(TaskTodo::getPlanStartTime, startDate); + } + if (StrUtil.isNotBlank(taskState)) { + queryWrapper.eq(TaskTodo::getTaskState, taskState); + } + queryWrapper.ne(TaskTodo::getTaskType, "4"); + queryWrapper.orderByAsc(TaskTodo::getPlanStartTime).orderByAsc(TaskTodo::getTaskTodoId); + List list = this.list(queryWrapper); + String now = DateUtil.now(); + Date parse = DateUtil.parse(now); + List collect = list.stream().map(TaskTodo::getTaskState).collect(Collectors.toList()); + if (collect.contains("2")) { + return (collect.indexOf("2") + 1); + } else { + for (int i = 0; i < list.size(); i++) { + TaskTodo taskTodo = list.get(i); + String planStartTime = taskTodo.getPlanStartTime(); + Date date = DateUtil.parse(planStartTime); + if ("0".equals(taskTodo.getTaskState()) && parse.before(date)) { + return (i + 1); + } + } + } + return 0; + } + + /********************************** + * 用途说明: 获取两个日期之间的所有日期 + * 参数说明 startTime 开始时间 + * 参数说明 endTime 结束时间 + * 返回值说明: java.util.List + ***********************************/ + public static List getDays(String startDate, String endDate) { + // 返回的日期集合 + List days = new ArrayList<>(); + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + try { + Date start = dateFormat.parse(startDate); + Date end = dateFormat.parse(endDate); + + Calendar tempStart = Calendar.getInstance(); + tempStart.setTime(start); + + Calendar tempEnd = Calendar.getInstance(); + tempEnd.setTime(end); + // 日期加1(包含结束) + tempEnd.add(Calendar.DATE, +1); + while (tempStart.before(tempEnd)) { + days.add(dateFormat.format(tempStart.getTime())); + tempStart.add(Calendar.DAY_OF_YEAR, 1); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + return days; + } + + @Override + public boolean updatePatrolDeviceState(String stationcode, String patroldeviceCcode, String isworking) { + List list = patroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getInternationalId, patroldeviceCcode)); + String code = patroldeviceCcode; + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + String channelinfo = substationPatroldevice.getChannelinfo(); + if (JSONUtil.isTypeJSONArray(channelinfo)) { + JSONArray jarray = JSONUtil.parseArray(channelinfo); + for (int i = 0; i < jarray.size(); i++) { + JSONObject jobj = jarray.getJSONObject(i); + if (jobj.getStr("channel_type").equals("2")) { //红外 + if (ObjUtil.isNotEmpty(jobj.getStr("pan_code"))) { + code = jobj.getStr("pan_code"); + } + } + } + } + } + String rediskey = RiisConstants.Patroldevice_IsWorking + stationcode + "_" + code; + if ("1".equals(isworking)) { + redisTemplate.delete(rediskey); + redisTemplate.opsForValue().set(rediskey, "1", 1, TimeUnit.MINUTES); + } else { + redisTemplate.delete(rediskey); + // redisTemplate.expire(rediskey, ,TimeUnit.SECONDS); + } + return true; + } + + /********************************** + * 用途说明: 根据任务执行情况ID查询点位查询 + * 参数说明 taskTodoId + * 参数说明 deviceName + * 参数说明 valid + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getTaskDeviceById(String taskTodoId, String deviceName, String valid) { + return taskResultMapper.getTaskDeviceById(taskTodoId, deviceName, valid); + } + + /********************************** + * 用途说明: 查询联动任务列表 + * 参数说明 page + * 参数说明 stationCode + * 参数说明 linkageDeviceName + * 参数说明 taskName + * 参数说明 linkageType + * 参数说明 linkageDeviceType + * 参数说明 startFormat + * 参数说明 endFormat + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getLinkTaskTodoList(Page> page, String stationCode, + String linkageDeviceName, String taskName, + String linkageType, String linkageDeviceType, + String startDate, String endDate) { + return taskTodoMapper.getLinkTaskTodoList(page, stationCode, linkageDeviceName, taskName, linkageType, + linkageDeviceType, startDate, endDate); + } + + /********************************** + * 用途说明: 获取点位数量 + * 参数说明 taskTodoId + * 返回值说明: long + ***********************************/ + @Override + public long getTaskResultCount(String taskTodoId) { + return taskResultMapper.selectCount( + new LambdaQueryWrapper(). + eq(TaskResult::getTaskTodoId, taskTodoId). + notIn(TaskResult::getFlag, "0", "1") + ); + } + + @Override + public List getAbnormalResult(String taskTodoId) { + return taskResultMapper.getAbnormalResult(taskTodoId); + } + + /********************************** + * 用途说明: 巡视设备巡检统计 + * 参数说明 page + * 参数说明 patrolDeviceCode + * 参数说明 patrolDeviceName + * 参数说明 type + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getDeviceInspectionStat(Page> page, String patrolDeviceCode, + String patrolDeviceName, String type) { + return taskResultMapper.getDeviceInspectionStat(page, patrolDeviceCode, patrolDeviceName, type); + } + + /********************************** + * 用途说明: 巡视设备巡检统计(上报上级) + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public List> getDeviceInspectionStat() { + return taskResultMapper.getPatrolDeviceInspectionStat(); + } + + /********************************** + * 用途说明: 巡视任务数据统计 + * 参数说明 page + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getTaskDataStat(Page> page, String stationCode, String type, + String startDate, + String endDate) { + + return taskResultMapper.getTaskDataStat(page, stationCode, type, startDate, endDate); + + } + + @Override + public List> getTaskDataStat(String stationCode, String type, String startDate, + String endDate) { + return taskResultMapper.getTaskDataStat("", type, startDate, endDate); + } + + /********************************** + * 用途说明: 根据任务id查询未审核告警和数量 + * 参数说明 taskTodoIds + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getNotCheckAlarmInfo(String taskTodoIds) { + return taskResultMapper.getNotCheckAlarmInfo(taskTodoIds); + } + + /********************************** + * 用途说明: 设置实物ID + * 参数说明 resultId + * 参数说明 filePath + * 返回值说明: void + ***********************************/ + @Override + public void setMaterialIdByImg(String resultId, String filePath) { + String content = FileUtil.resolveCodeByFile(filePath); + if (StrUtil.isNotBlank(content)) { + TaskResult taskResult = new TaskResult(); + taskResult.setResultId(resultId); + taskResult.setMaterialId(content); + this.updateTaskResultById(taskResult); + } + } + + /********************************** + * 用途说明: 调用温度的sdk识别并给图片加上温度 + * 参数说明 resultId + * 参数说明 objectId + * 参数说明 patrolDeviceCode + * 参数说明 fullfilename + * 返回值说明: com.alibaba.fastjson.JSONObject + ***********************************/ + @Override + public com.alibaba.fastjson.JSONObject callPicAnalyse(String resultId, String objectId, String patrolDeviceCode, + String rtspurl, String fullfilename, String region, String csvFilePath) throws IOException { + log.info(region); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getInternationalId, patrolDeviceCode); + SubstationPatroldevice substationPatroldevice = patroldeviceMapper.selectOne(queryWrapper); + String manufacturer = substationPatroldevice.getManufacturer(); + if ("haikang".equals(manufacturer) && StrUtil.isNotBlank(rtspurl)) { + String reg = "//([^/]+)"; + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(rtspurl); + if (!matcher.find()) { + throw new RuntimeException("rtsp地址错误"); + } + String group = matcher.group(1); + int lastAtIndex = group.lastIndexOf('@'); + // 如果找到了@ + if (lastAtIndex != -1) { + // 检查@后面是否紧跟着IP地址和端口号,但在这个例子中我们假设没有 + // 所以直接替换最后一个@为: + group = group.substring(0, lastAtIndex) + ":" + group.substring(lastAtIndex + 1); + } else { + log.error("rtsp地址错误"); + } + String[] split = group.split(":"); + String username = split[0]; + String password = split[1]; + String ip = split[2]; + log.info("-------调用callPicAnalyse方法--------"); + return getHaiKangTemperature(resultId, objectId, fullfilename, manufacturer, ip, (short) 8000, username, + password, region, csvFilePath); + } + + if ("dahua".equals(manufacturer) && StrUtil.isNotBlank(rtspurl)) { + TaskResult taskResult = taskResultMapper.selectById(objectId); + SubstationDevice substationDevice = substationDeviceService.getById(taskResult.getDeviceId()); + String reg = "//([^/]+)"; + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(rtspurl); + if (!matcher.find()) { + throw new RuntimeException("rtsp地址错误"); + } + String group = matcher.group(1); + int lastAtIndex = group.lastIndexOf('@'); + // 如果找到了@ + if (lastAtIndex != -1) { + // 检查@后面是否紧跟着IP地址和端口号,但在这个例子中我们假设没有 + // 所以直接替换最后一个@为: + group = group.substring(0, lastAtIndex) + ":" + group.substring(lastAtIndex + 1); + } else { + log.error("rtsp地址错误"); + } + String[] split = group.split(":"); + String username = split[0]; + String password = split[1]; + String ip = split[2]; + log.info("-------调用callPicAnalyse方法--------"); + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + if (jsonArray.size() > 0) { + JSONObject jsonObject = jsonArray.getJSONObject(0); + int presetId = jsonObject.getInt("patroldevice_pos"); + return getDaHuaTemperature(resultId, objectId, fullfilename, ip, 37777, username, password, + substationDevice.getOutsideAngle(), presetId); + } + + } + if ("dali".equals(manufacturer) && StrUtil.isNotBlank(rtspurl)) { + return CameraUtil.callPicAnalyse(resultId, objectId, fullfilename, manufacturer, + substationPatroldevice.getIpaddr(), (short) 80, null, null, region, csvFilePath); + } + + if ("heika".equals(manufacturer)) { + String reg = "//([^/]+)"; + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(rtspurl); + if (!matcher.find()) { + throw new RuntimeException("rtsp地址错误"); + } + String group = matcher.group(1); + int lastAtIndex = group.lastIndexOf('@'); + // 如果找到了@ + if (lastAtIndex != -1) { + // 检查@后面是否紧跟着IP地址和端口号,但在这个例子中我们假设没有 + // 所以直接替换最后一个@为: + group = group.substring(0, lastAtIndex) + ":" + group.substring(lastAtIndex + 1); + } else { + log.error("rtsp地址错误"); + } + String[] split = group.split(":"); + String username = split[0]; + String password = split[1]; + String ip = split[2]; + log.info("-------调用callPicAnalyse方法--------"); + return getHeiKaTemperature(resultId, objectId, fullfilename, manufacturer, ip, (short) 8000, username, + password, region, csvFilePath); + } + return null; + } + + private com.alibaba.fastjson.JSONObject getHeiKaTemperature(String resultId, String objectId, String fullfilename + , String manufacturer, String ip, short port, String username, String password, String region, + String csvFilePath) { + // 获取spring bean 中 HttpServerConfig 类实例 + HttpServerConfig config = + SpringContextHolder.getBean(HttpServerConfig.class); + // 获取spring bean 中 HttpRESTfulUtils 类实例 + HttpRESTfulUtils httpUtil = + SpringContextHolder.getBean(HttpRESTfulUtils.class); + + String api = "getTemp"; + if (StrUtil.isNotBlank(config.getHeikaApp())) { + api = config.getHeikaApp() + "/getTemp"; + } + Map param = new HashMap<>(); + param.put("resultId", resultId); + param.put("objectId", objectId); + param.put("ip", ip); + param.put("port", port); + param.put("username", username); + param.put("password", password); + param.put("region", region); + param.put("csvFilePath", csvFilePath); + param.put("fullfilename", fullfilename); + com.alibaba.fastjson.JSONObject result = new com.alibaba.fastjson.JSONObject(); + if (StrUtil.isBlank(region)) { + result.put("code", "201"); + return result; + } + com.alibaba.fastjson.JSONObject resultObject = httpUtil.sendHttpPost("json", config.getSdkIp(), + config.getHeikaPort(), "", api, param, null); + return null; + } + + public static com.alibaba.fastjson.JSONObject getDaHuaTemperature(String requestId, String objectId, + String filePath, String ip, + int port, String username, String password, + String outsideAngle, + int presetId) { + // 获取spring bean 中 HttpServerConfig 类实例 + HttpServerConfig config = + SpringContextHolder.getBean(HttpServerConfig.class); + // 获取spring bean 中 HttpRESTfulUtils 类实例 + HttpRESTfulUtils httpUtil = + SpringContextHolder.getBean(HttpRESTfulUtils.class); + + String api = "getTempItem"; + if (StrUtil.isNotBlank(config.getDahuaApp())) { + api = config.getDahuaApp() + "/getTempItem"; + } + Map param = new HashMap<>(); + param.put("ip", ip); + param.put("port", port); + param.put("username", username); + param.put("password", password); + param.put("presetId", presetId); + param.put("channel", 1); + com.alibaba.fastjson.JSONObject result = new com.alibaba.fastjson.JSONObject(); + if (StrUtil.isBlank(outsideAngle)) { + result.put("code", "201"); + return result; + } + com.alibaba.fastjson.JSONObject dataPos = com.alibaba.fastjson.JSONObject.parseObject(outsideAngle); + String type = dataPos.getString("type"); + if ("point".equals(type)) { + param.put("type", 1); + } + if ("line".equals(type)) { + param.put("type", 2); + } + if ("rectangle".equals(type)) { + param.put("type", 3); + } + param.put("ruleId", dataPos.getInteger("ruleId")); + com.alibaba.fastjson.JSONObject resultObject = httpUtil.sendHttpPost("json", config.getSdkIp(), + config.getDahuaPort(), "", api, param, + null); + + if (!"0".equals(resultObject.getString("code"))) { + result.put("code", "201"); + return result; + } + com.alibaba.fastjson.JSONObject data = resultObject.getJSONObject("data"); + String temperature = ""; + if ("point".equals(type)) { + String aver = data.getString("aver"); + double averD = NumberUtil.parseDouble(aver); + String averStr = String.format("%.1f", averD); + temperature = averStr + "," + averStr; + } + if ("line".equals(type) || "rectangle".equals(type)) { + String min = data.getString("min"); + String max = data.getString("max"); + double minD = NumberUtil.parseDouble(min); + double maxD = NumberUtil.parseDouble(max); + temperature = String.format("%.1f", minD) + "," + String.format("%.1f", maxD); + } + + com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject(); + jsonObject.put("requestId", requestId); + List> mapList = new ArrayList<>(); + Map map = new HashMap<>(); + map.put("objectId", objectId); + List> mapList1 = new ArrayList<>(); + Map map1 = new HashMap<>(); + map1.put("type", "infrared"); + map1.put("value", temperature); + if (StrUtil.isBlank(temperature)) { + result.put("code", "201"); + map1.put("code", "2001"); + } else { + result.put("code", "200"); + map1.put("code", "2000"); + map1.put("conf", "100"); + String imageurl = String.format("http://%s:%s/snapfile?filename=%s", config.getPatrolIp(), + config.getPatrolPort(), filePath); + if (StrUtil.isNotEmpty(config.getPatrolAppname())) { + imageurl = String.format("http://%s:%s/%s/snapfile?filename=%s", config.getPatrolIp(), + config.getPatrolPort(), config.getPatrolAppname(), filePath); + } + map1.put("resImagePath", imageurl); + } + map1.put("desc", temperature); + + com.alibaba.fastjson.JSONArray pos = dataPos.getJSONArray("pos"); + JSONArray posArray = new JSONArray(); + if ("rectangle".equals(dataPos.getString("type"))) { + // 左下角 + com.alibaba.fastjson.JSONObject ojb1 = pos.getJSONObject(0); + Integer x1 = ojb1.getInteger("x"); + Integer y1 = ojb1.getInteger("y"); + ojb1.put("x", x1); + ojb1.put("y", y1); + // 右上角 + com.alibaba.fastjson.JSONObject ojb2 = pos.getJSONObject(2); + Integer x2 = ojb2.getInteger("x"); + Integer y2 = ojb2.getInteger("y"); + ojb2.put("x", x2); + ojb2.put("y", y2); + posArray.add(ojb1); + log.info(ojb1.toString()); + posArray.add(ojb2); + log.info(ojb2.toString()); + } + if ("point".equals(dataPos.getString("type"))) { + // 点坐标 + com.alibaba.fastjson.JSONObject ojb = pos.getJSONObject(0); + Integer x1 = ojb.getInteger("x"); + Integer y1 = ojb.getInteger("y"); + ojb.put("x", x1); + ojb.put("y", y1); + posArray.add(ojb); + } + JSONArray dataArray = new JSONArray(); + com.alibaba.fastjson.JSONObject dataJson = new com.alibaba.fastjson.JSONObject(); + dataJson.put("coors", posArray); + dataArray.add(dataJson); + map1.put("pos", dataArray); + mapList1.add(map1); + map.put("results", mapList1); + mapList.add(map); + jsonObject.put("resultList", mapList); + String api1 = "picAnalyseRetNotify"; + if (StrUtil.isNotBlank(config.getPatrolAppname())) { + api1 = "riis-system/picAnalyseRetNotify"; + } + httpUtil.sendHttpPost("json", config.getPatrolIp(), config.getPatrolPort(), "", api1, jsonObject, null); + return result; + } + + public static com.alibaba.fastjson.JSONObject getHaiKangTemperature(String requestId, String objectId, + String filePath, String manufacturer, + String ip, short port, String username, + String password, + String outsideAngle, String csvFilePath) { + // 获取spring bean 中 HttpServerConfig 类实例 + HttpServerConfig config = + SpringContextHolder.getBean(HttpServerConfig.class); + // 获取spring bean 中 HttpRESTfulUtils 类实例 + HttpRESTfulUtils httpUtil = + SpringContextHolder.getBean(HttpRESTfulUtils.class); + + String api = "getTempItem"; + if (StrUtil.isNotBlank(config.getHaikangApp())) { + api = config.getHaikangApp() + "/getTemperature"; + } + Map param = new HashMap<>(); + param.put("ip", ip); + param.put("port", port); + param.put("username", username); + param.put("password", password); + param.put("manufacturer", manufacturer); + param.put("outsideAngle", outsideAngle); + com.alibaba.fastjson.JSONObject result = new com.alibaba.fastjson.JSONObject(); + File srcImgFile = new File(csvFilePath); + //文件转化为图片 + Image srcImg = null; + try { + srcImg = ImageIO.read(srcImgFile); + } catch (IOException e) { + log.error(e.getMessage());; + } + if (srcImg == null) { + result.put("code", "201"); + return result; + } + //获取图片的宽 + int srcImgWidth = srcImg.getWidth(null); + //获取图片的高 + int srcImgHeight = srcImg.getHeight(null); + param.put("srcImgHeight", srcImgHeight); + param.put("srcImgWidth", srcImgWidth); + if (StrUtil.isBlank(outsideAngle)) { + result.put("code", "201"); + return result; + } + com.alibaba.fastjson.JSONObject resultObject = httpUtil.sendHttpPost("json", config.getSdkIp(), + config.getHaikangPort(), "", api, param, null); + if (!"0".equals(resultObject.getString("code"))) { + result.put("code", "201"); + return result; + } + log.info(resultObject.toString()); + log.info(outsideAngle); + String temperature = resultObject.getString("data"); + com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject(); + jsonObject.put("requestId", requestId); + List> mapList = new ArrayList<>(); + Map map = new HashMap<>(); + map.put("objectId", objectId); + List> mapList1 = new ArrayList<>(); + Map map1 = new HashMap<>(); + map1.put("type", "infrared"); + map1.put("value", temperature); + if (StrUtil.isBlank(temperature)) { + result.put("code", "201"); + map1.put("code", "2001"); + } else { + result.put("code", "200"); + map1.put("code", "2000"); + map1.put("conf", "100"); + String imageurl = String.format("http://%s:%s/snapfile?filename=%s", config.getPatrolIp(), + config.getPatrolPort(), filePath); + if (StrUtil.isNotEmpty(config.getPatrolAppname())) { + imageurl = String.format("http://%s:%s/%s/snapfile?filename=%s", config.getPatrolIp(), + config.getPatrolPort(), config.getPatrolAppname(), filePath); + } + map1.put("resImagePath", imageurl); + } + map1.put("desc", temperature); + JSONObject dataPos = JSONUtil.parseObj(outsideAngle); + JSONArray pos = dataPos.getJSONArray("pos"); + JSONArray posArray = new JSONArray(); + if ("rectangle".equals(dataPos.getStr("type"))) { + // 左下角 + JSONObject ojb1 = pos.getJSONObject(0); + // 右上角 + JSONObject ojb2 = pos.getJSONObject(2); + posArray.add(ojb1); + posArray.add(ojb2); + } + if ("point".equals(dataPos.getStr("type"))) { + // 点坐标 + JSONObject ojb = pos.getJSONObject(0); + posArray.add(ojb); + } + JSONArray dataArray = new JSONArray(); + JSONObject dataJson = new JSONObject(); + dataJson.putOnce("coors", posArray); + dataArray.add(dataJson); + map1.put("pos", dataArray); + mapList1.add(map1); + map.put("results", mapList1); + mapList.add(map); + jsonObject.put("resultList", mapList); + String api1 = "picAnalyseRetNotify"; + if (StrUtil.isNotBlank(config.getPatrolAppname())) { + api1 = "riis-system/picAnalyseRetNotify"; + } + httpUtil.sendHttpPost("json", config.getPatrolIp(), config.getPatrolPort(), "", api1, jsonObject, null); + return result; + } + + /********************************** + * 用途说明: 调用温度的sdk识别并给图片加上温度 + * 参数说明 resultId + * 参数说明 objectId + * 参数说明 patrolDeviceCode + * 参数说明 fullfilename + * 返回值说明: com.alibaba.fastjson.JSONObject + ***********************************/ + @Override + public com.alibaba.fastjson.JSONObject callSoundAnalyse(String resultId, String objectId) throws IOException { + com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject(); + jsonObject.put("requestId", resultId); + List> mapList = new ArrayList<>(); + Map map = new HashMap<>(); + map.put("objectId", objectId); + List> mapList1 = new ArrayList<>(); + Map map1 = new HashMap<>(); + map1.put("type", "sound"); + map1.put("value", 1); + jsonObject.put("code", "200"); + map1.put("code", "2000"); + map1.put("conf", "1"); + map1.put("resImagePath", ""); + map1.put("desc", "声纹检测"); + mapList1.add(map1); + map.put("results", mapList1); + mapList.add(map); + jsonObject.put("resultList", mapList); + String api = "picAnalyseRetNotify"; + if (StrUtil.isNotBlank(httpServerConfig.getPatrolAppname())) { + api = "riis-system/picAnalyseRetNotify"; + } + httpUtil.sendHttpPost("json", httpServerConfig.getPatrolIp(), httpServerConfig.getPatrolPort(), "", api, + jsonObject, null); + return jsonObject; + } + + /********************************** + * 用途说明: 设置请求分析主机调用失败的任务为巡视失败 + * 参数说明 stationCode + * 参数说明 resultId + * 参数说明 deviceSumnum + * 参数说明 taskTodoId + * 返回值说明: void + ***********************************/ + @Override + public synchronized void setTaskResultAndTodo(String stationCode, String resultId, Integer deviceSumnum, String taskTodoId) { + TaskResult taskResult = new TaskResult(); + taskResult.setResultId(resultId); + taskResult.setFlag("4"); + this.updateTaskResultById(taskResult); + long taskResultCount = this.getTaskResultCount(taskTodoId); + TaskTodo taskTodo = this.getById(taskTodoId); + // 赋值执行数量 + List> list = this.getTaskResultById(taskTodo.getTaskTodoId(), null, null, null); + // 已送检点数 + long patrolCount = list.stream().filter(t -> !("0").equals(t.get("flag")) && !("1").equals(t.get("flag"))).count(); + // 失败的数量 + long failCount = + list.stream().filter(t -> ("4").equals(t.get("flag")) || ("6").equals(t.get("flag"))).count(); + // 异常数量 + long abnormalCount = list.stream().filter(t -> ("2").equals(t.get("valid1"))).count(); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(TaskTodo::getTaskTodoId, taskTodoId).set(TaskTodo::getDeviceDeforeNum, patrolCount).set(TaskTodo::getDeviceFailureNum, failCount).set(TaskTodo::getDeviceUnusualnum, abnormalCount); + boolean b = deviceSumnum == taskResultCount; + if (b) { + //taskTodo.setTaskState("1"); //已完成 + updateWrapper.set(TaskTodo::getTaskState, "1").set(TaskTodo::getEndTime, DateUtil.now()); + } + this.update(updateWrapper); + JSONObject jsonObject = new JSONObject(); + jsonObject.putOnce("taskTodoId", taskTodoId); + jsonObject.putOnce("taskType", taskTodo.getTaskType()); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), jsonObject.toString()); + + // WebSocketServer.sendInfo("taskresult_" + stationCode, "msg"); + } + + /********************************** + * 用途说明: 查询联动任务最近十条 + * 参数说明 linkageType 联动类型 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getLinkTaskRecordLast(String stationCode, String linkageType, String taskName) { + return taskTodoMapper.getLinkTaskRecordLast(stationCode, linkageType, taskName); + } + + /********************************** + * 用途说明: 查询联动控制巡视任务 + * 参数说明 stationCode + * 参数说明 todoId + * 参数说明 linkageType + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getLinkTaskTodo(String stationCode, String todoId, String linkageType) { + return taskTodoMapper.getLinkTaskTodo(stationCode, todoId, linkageType); + } + + /********************************** + * 用途说明: 按照日、周、月统计巡视任务 + * 参数说明 type + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getTaskDurationStat(String startDate, String endDate, String type) { + String startFormat = ""; + String endFormat = ""; + if ("3".equals(type)) { + startDate = startDate + "-01"; + Date parseStart = DateUtil.parse(startDate); + Date beginOfDay = DateUtil.beginOfMonth(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + endDate = endDate + "-01"; + Date parseEnd = DateUtil.parse(endDate); + //一月的结束 + Date endOfDay = DateUtil.endOfMonth(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } else { + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + } + return taskTodoMapper.getTaskDurationStat(startFormat, endFormat, type); + } + + /********************************** + * 用途说明: 按照日、周、月统计巡视任务导出 + * 参数说明 startDate + * 参数说明 endDate + * 参数说明 type + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void exportTaskDurationStat(String startDate, String endDate, String type, HttpServletResponse response) throws IOException { + List> taskDurationStat = this.getTaskDurationStat(startDate, endDate, type); + taskDurationStat.forEach(t -> { + t.put("日期", t.remove("stattime")); + t.put("巡视任务次数", t.remove("num")); + t.put("巡视时长", t.remove("duration")); + t.put("发现缺陷种类", t.remove("type")); + t.put("发现缺陷数量", t.remove("count")); + }); + FileUtil.downloadExcel(taskDurationStat, response); + } + + /********************************** + * 用途说明: 巡视任务数据统计 + * 参数说明 page + * 参数说明 stationCode + * 参数说明 type + * 参数说明 startDate + * 参数说明 endDate + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void exportTaskDataStat(Page> page, String stationCode, String type, String startDate, + String endDate, HttpServletResponse response) throws IOException { + String startFormat = ""; + String endFormat = ""; + if ("3".equals(type)) { + startDate = startDate + "-01"; + Date parseStart = DateUtil.parse(startDate); + Date beginOfDay = DateUtil.beginOfMonth(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + endDate = endDate + "-01"; + Date parseEnd = DateUtil.parse(endDate); + //一月的结束 + Date endOfDay = DateUtil.endOfMonth(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } else { + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + } + + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + } + } + Page> mapPage = this.getTaskDataStat(page, stationCode, type, startFormat, endFormat); + List> records = mapPage.getRecords(); + records.forEach(r -> { + String date = r.get("date").toString(); + r.put("time", date); + if (ObjectUtil.isNotEmpty(r.get("count1")) && ObjectUtil.isNotEmpty(r.get("sum1"))) { + String count1 = r.get("count1").toString(); + String sum1 = r.get("sum1").toString(); + String format = String.format("%.2f", + ((float) Integer.parseInt(sum1) * 100 / Integer.parseInt(count1))); + r.put("countRate1", format + "%"); + } else { + r.put("countRate1", "0%"); + } + + if (ObjectUtil.isNotEmpty(r.get("count2")) && ObjectUtil.isNotEmpty(r.get("sum2"))) { + String count2 = r.get("count2").toString(); + String sum2 = r.get("sum2").toString(); + String format = String.format("%.2f", + ((float) Integer.parseInt(sum2) * 100 / Integer.parseInt(count2))); + r.put("countRate2", format + "%"); + } else { + r.put("countRate2", "0%"); + } + + if (ObjectUtil.isNotEmpty(r.get("count3")) && ObjectUtil.isNotEmpty(r.get("sum3"))) { + String count3 = r.get("count3").toString(); + String sum3 = r.get("sum3").toString(); + String format = String.format("%.2f", + ((float) Integer.parseInt(sum3) * 100 / Integer.parseInt(count3))); + r.put("countRate3", format + "%"); + } else { + r.put("countRate3", "0%"); + } + if (ObjectUtil.isNotEmpty(r.get("count4")) && ObjectUtil.isNotEmpty(r.get("sum4"))) { + String count4 = r.get("count4").toString(); + String sum4 = r.get("sum4").toString(); + String format = String.format("%.2f", + ((float) Integer.parseInt(sum4) * 100 / Integer.parseInt(count4))); + r.put("countRate4", format + "%"); + } else { + r.put("countRate4", "0%"); + } + + if (ObjectUtil.isNotEmpty(r.get("count5")) && ObjectUtil.isNotEmpty(r.get("sum5"))) { + String count5 = r.get("count5").toString(); + String sum5 = r.get("sum5").toString(); + String format = String.format("%.2f", + ((float) Integer.parseInt(sum5) * 100 / Integer.parseInt(count5))); + r.put("countRate5", format + "%"); + } else { + r.put("countRate5", "0%"); + } + if (ObjectUtil.isNotEmpty(r.get("num"))) { + r.put("num", r.get("num").toString()); + } + if (ObjectUtil.isNotEmpty(r.get("duration"))) { + r.put("duration", r.get("duration").toString()); + } + if (ObjectUtil.isNotEmpty(r.get("type"))) { + r.put("type", r.get("type").toString()); + } + if (ObjectUtil.isNotEmpty(r.get("count"))) { + r.put("count", r.get("count").toString()); + } + }); + + String[] headers = {"时间", "巡视点位漏检率", "巡视任务执行闭环率", "巡视告警人工审核完成率", "巡视告警准确率", "巡视结果人工审核完成率", "巡视任务的次数", "巡视时长", + "发现缺陷种类", "发现缺陷数量"}; + String[] keys = {"time", "countRate1", "countRate5", "countRate2", "countRate4", "countRate3", "num", + "duration", "type", "count"}; + FileUtil.excelImg(records, headers, keys, "", "", response); + } + + /********************************** + * 用途说明: 获取历史点位 + * 参数说明 startDate 开始时间 + * 参数说明 endDate 结束时间 + * 参数说明 deviceId 点位id + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getHistoryDevice(Page> page, String startDate, + String endDate, String deviceId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + String startFormat = ""; + queryWrapper.ne(TaskResult::getFlag, "0"); + if (StrUtil.isNotBlank(startDate)) { + Date parseStart = DateUtil.parse(startDate); + //一天的开始 + Date beginOfDay = DateUtil.beginOfDay(parseStart); + startFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd HH:mm:ss"); + queryWrapper.ge(TaskResult::getPatroldeviceDate, startFormat); + } + String endFormat = ""; + if (StrUtil.isNotBlank(startDate)) { + Date parseEnd = DateUtil.parse(endDate); + //一天的结束 + Date endOfDay = DateUtil.endOfDay(parseEnd); + endFormat = DateUtil.format(endOfDay, "yyyy-MM-dd HH:mm:ss"); + queryWrapper.le(TaskResult::getPatroldeviceDate, endFormat); + } + if (StrUtil.isNotBlank(deviceId)) { + queryWrapper.eq(TaskResult::getDeviceId, deviceId); + } + queryWrapper.orderByAsc(TaskResult::getPatroldeviceDate); + return taskResultMapper.selectMapsPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 查询非同源对比分析列表 + * 参数说明 stationCode 变电站编号 + * 参数说明 taskName 任务名称 + * 参数说明 mainDeviceName 主设备名称 + * 参数说明 componentName 部件名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getNonHomologousAnalysis(String stationCode, String taskName, + String mainDeviceName, String componentName, + String recognitionType, + String startFormat, String endFormat) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 只针对红外、表记、位置 + queryWrapper.in(TaskResult::getRecognitionType, "1", "2", "4"); + queryWrapper.eq(TaskResult::getFlag, "2"); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.eq(TaskResult::getStationCode, stationCode); + } + if (StrUtil.isNotBlank(taskName)) { + queryWrapper.like(TaskResult::getTaskName, taskName); + } + if (StrUtil.isNotBlank(mainDeviceName)) { + queryWrapper.like(TaskResult::getMainDeviceName, mainDeviceName); + } + if (StrUtil.isNotBlank(componentName)) { + queryWrapper.like(TaskResult::getComponentName, componentName); + } + if (StrUtil.isNotBlank(recognitionType)) { + queryWrapper.eq(TaskResult::getRecognitionType, recognitionType); + } + if (StrUtil.isNotBlank(startFormat)) { + queryWrapper.ge(TaskResult::getPatroldeviceDate, startFormat); + } + if (StrUtil.isNotBlank(endFormat)) { + queryWrapper.le(TaskResult::getPatroldeviceDate, endFormat); + } + queryWrapper.orderByAsc(TaskResult::getTaskCode, TaskResult::getDeviceName, TaskResult::getOrderNum); + List> mapList = taskResultMapper.selectMaps(queryWrapper); + List> analysisList = new ArrayList<>(); + // 按照任务名称分组 + Map>> taskNameList = + mapList.stream().collect(Collectors.groupingBy(m -> m.get("deviceName").toString() + "(" + m.get( + "taskName").toString() + ")" + "_" + m.get("taskTodoId").toString(), Collectors.toList())); + for (String name : taskNameList.keySet()) { + Map map = new HashMap<>(); + List> deviceList = taskNameList.get(name); + // 点位绑定两个摄像头或以上则进行数据对比分析 + if (deviceList.size() != 2) { + continue; + } + deviceList.forEach(r -> { + String v = ObjectUtil.isNotEmpty(r.get("valid")) ? r.get("valid").toString() : ""; + // 结果 + String valid; + if (Objects.isNull(r.get("reviseValue"))) { + valid = v; + } else { + if ("0".equals(r.get("reviseValid").toString())) { + valid = "1"; + } else { + valid = "2"; + } + } + String flag = r.get("flag").toString(); + if ("4".equals(flag)) { + r.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + r.put("analysisResult", "采集失败"); + } else if (StrUtil.isNotBlank(valid) && "1".equals(valid)) { + r.put("analysisResult", "正常"); + } else if (StrUtil.isNotBlank(valid) && "2".equals(valid)) { + r.put("analysisResult", "异常"); + } else { + r.put("analysisResult", ""); + } + }); + String difference = ""; + Map result1 = deviceList.get(0); + Map result2 = deviceList.get(1); + if (ObjectUtil.isEmpty(result1.get("recognitionType"))) { + continue; + } + if ("1".equals(result1.get("recognitionType").toString()) && ObjectUtil.isNotEmpty(result1.get("value")) && ObjectUtil.isNotEmpty(result2.get("value"))) { + // 表计 + BigDecimal value1 = new BigDecimal(result1.get("value").toString()); + BigDecimal value2 = new BigDecimal(result2.get("value").toString()); + difference = Math.abs(value1.subtract(value2).doubleValue()) + ""; + } + + // 测温 + if ("4".equals(result1.get("recognitionType").toString()) && ObjectUtil.isNotEmpty(result1.get("value")) && ObjectUtil.isNotEmpty(result2.get("value"))) { + String value = result1.get("value").toString(); + String value1 = result2.get("value").toString(); + String[] split = value.split(","); + String[] split1 = value1.split(","); + if (split.length != 2 || split1.length != 2) { + break; + } + // BigDecimal v1 = new BigDecimal(split[0]); + BigDecimal v2 = new BigDecimal(split[0]); + // BigDecimal v3 = new BigDecimal(split1[0]); + BigDecimal v4 = new BigDecimal(split1[0]); + difference = Math.abs(v2.subtract(v4).doubleValue()) + ""; + } + if ("2".equals(result1.get("recognitionType").toString())) { + // 表面 + String desc1 = ObjectUtil.isNotEmpty(result1.get("desc")) ? result1.get("desc").toString() : ""; + String desc2 = ObjectUtil.isNotEmpty(result2.get("desc")) ? result2.get("desc").toString() : ""; + if (StrUtil.isBlank(desc1) && StrUtil.isBlank(desc2)) { + difference = ""; + } else if (desc1.equals(desc2)) { + difference = "相同"; + } else { + difference = "不相同"; + } + } + String deviceId = result1.get("deviceId").toString(); + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + if (StrUtil.isNotBlank(patroldeviceJson)) { + JSONArray jsonArray = JSONUtil.parseArray(patroldeviceJson); + JSONObject jsonObject = jsonArray.getJSONObject(1); + String alertvalue = jsonObject.getStr("alertvalue"); + map.put("alertvalue", alertvalue); + } + String title = StrUtil.subBefore(name, "_", false); + map.put("difference", difference); + map.put("taskName", title); + map.put("deviceName", name); + map.put("resultList", deviceList); + analysisList.add(map); + + } + return analysisList; + } + + /********************************** + * 用途说明: 查询非同相对比分析列表 + * 参数说明 stationCode 变电站编号 + * 参数说明 taskName 任务名称 + * 参数说明 mainDeviceName 主设备名称 + * 参数说明 componentName 部件名名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getNonCoherentAnalysis(String stationCode, String taskName, + String mainDeviceName, String componentName, + String recognitionType, String startFormat, + String endFormat) { + List> mapList = taskResultMapper.getNonCoherentAnalysis("", stationCode, taskName, + mainDeviceName, componentName, recognitionType, startFormat, endFormat); + Map>> taskList = + mapList.stream().collect(Collectors.groupingBy(m -> m.get("taskName").toString() + "" + m.get( + "recognitionType").toString() + "_" + m.get("meterType").toString() + "_" + m.get("startTime").toString(), Collectors.toList())); + List> nonCoherentList = new ArrayList<>(); + for (String key : taskList.keySet()) { + List> resultList = taskList.get(key); + // 结果小于2直接跳过 + if (resultList.size() < 2) { + continue; + } + // 获取相位分组数量 + Set phases = resultList.stream().map(r -> r.get("phase").toString()).collect(Collectors.toSet()); + Set deviceIds = + resultList.stream().map(m -> m.get("deviceId").toString()).collect(Collectors.toSet()); + // 相位小于2直接跳过 + if (phases.size() != resultList.size() || deviceIds.size() != resultList.size()) { + continue; + } + resultList.forEach(r -> { + String v = ObjectUtil.isNotEmpty(r.get("valid")) ? r.get("valid").toString() : ""; + // 结果 + String valid; + if (Objects.isNull(r.get("reviseValue"))) { + valid = v; + } else { + if ("0".equals(r.get("reviseValid").toString())) { + valid = "1"; + } else { + valid = "2"; + } + } + String flag = r.get("flag").toString(); + if ("4".equals(flag)) { + r.put("analysisResult", "识别失败"); + } else if ("6".equals(flag)) { + r.put("analysisResult", "采集失败"); + } else if (StrUtil.isNotBlank(valid) && "1".equals(valid)) { + r.put("analysisResult", "正常"); + } else if (StrUtil.isNotBlank(valid) && "2".equals(valid)) { + r.put("analysisResult", "异常"); + } else { + r.put("analysisResult", ""); + } + }); + Map map = new LinkedHashMap<>(); + String name = StrUtil.subBefore(key, "_", false); + String startTime = StrUtil.subAfter(key, "_", true); + map.put("name", name + "(" + startTime + ")"); + resultList.sort(Comparator.comparing(r -> r.get("phase").toString())); + map.put("taskResultList", resultList); + nonCoherentList.add(map); + + } + return nonCoherentList; + } + + /********************************** + * 用途说明: 查询趋势变化点位列表 + * 参数说明 page 分页参数 + * 参数说明 stationCode 变电站名称 + * 参数说明 taskName 任务名称 + * 参数说明 mainDeviceName 主设备名称 + * 参数说明 componentName 部件名称 + * 参数说明 recognitionType 识别类型 + * 参数说明 deviceName 点位名称 + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: com.baomidou.mybatisplus.extension.plugins.pagination.Page> + ***********************************/ + @Override + public Page> getTrendChangeDeviceList(Page> page, String stationCode, + String mainDeviceName, + String componentName, String recognitionType, + String deviceName, String startFormat, + String endFormat) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.select("DISTINCT station_code,area_name,bay_name,main_device_name,component_name,recognition_type,device_id,device_name"); + if (StrUtil.isNotBlank(stationCode)) { + queryWrapper.eq("station_code", stationCode); + } + if (StrUtil.isNotBlank(mainDeviceName)) { + queryWrapper.like("main_device_name", mainDeviceName); + } + if (StrUtil.isNotBlank(componentName)) { + queryWrapper.like("component_name", componentName); + } + if (StrUtil.isNotBlank(deviceName)) { + queryWrapper.like("device_name", deviceName); + } + if (StrUtil.isNotBlank(recognitionType)) { + queryWrapper.eq("recognition_type", recognitionType); + } + if (StrUtil.isNotBlank(startFormat)) { + queryWrapper.ge("patroldevice_date", startFormat); + } + if (StrUtil.isNotBlank(endFormat)) { + queryWrapper.le("patroldevice_date", endFormat); + } + queryWrapper.eq("flag", "2"); + + return taskResultMapper.selectMapsPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 查询趋势变化分析列表 + * 参数说明 deviceId 点位id + * 参数说明 startFormat 开始时间 + * 参数说明 endFormat 结束时间 + * 返回值说明: java.util.Map + ***********************************/ + @Override + public Map getTrendChangeAnalysisList(String deviceId, String startFormat, String endFormat) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(deviceId)) { + queryWrapper.eq(TaskResult::getDeviceId, deviceId); + } + if (StrUtil.isNotBlank(startFormat)) { + queryWrapper.ge(TaskResult::getPatroldeviceDate, startFormat); + } + if (StrUtil.isNotBlank(endFormat)) { + queryWrapper.le(TaskResult::getPatroldeviceDate, endFormat); + } + queryWrapper.eq(TaskResult::getFlag, "2"); + queryWrapper.isNotNull(TaskResult::getPatroldeviceDate); + queryWrapper.orderByAsc(TaskResult::getPatroldeviceDate); + queryWrapper.select(TaskResult::getResultId, TaskResult::getDeviceId, TaskResult::getDeviceName, + TaskResult::getPatroldeviceDate, TaskResult::getValue, TaskResult::getConf, TaskResult::getUnit, + TaskResult::getDesc); + List> mapList = taskResultMapper.selectMaps(queryWrapper); + // 按照日期分组 + Map>> taskResultMap = + mapList.stream().collect(Collectors.groupingBy(m -> DateUtil.format(DateUtil.parse(m.get( + "patroldeviceDate").toString()), "yyyy-MM-dd"))); + // 按照时间对map集合进行排序 + Map sortMap = new TreeMap<>(String::compareTo); + sortMap.putAll(taskResultMap); + Map map = new LinkedHashMap<>(); + List> deviceList = new ArrayList<>(); + map.put("date", sortMap.keySet()); + // 查询一个点位 + for (String date : sortMap.keySet()) { + List> resultList = taskResultMap.get(date); + Map result = resultList.get(resultList.size() - 1); + deviceList.add(result); + } + List value = deviceList.stream().map(m -> m.get("value").toString()).collect(Collectors.toList()); + //获取最大值 + String max = Collections.max(value); + //获取最小值 + String min = Collections.min(value); + if (StrUtil.isNotBlank(max) && StrUtil.isNotBlank(min)) { + // 计算差值 + BigDecimal bd1 = new BigDecimal(max); + BigDecimal bd2 = new BigDecimal(min); + double difference = bd1.subtract(bd2).doubleValue(); + map.put("max", max); + map.put("min", min); + map.put("difference", Math.abs(difference)); + } + SubstationDevice substationDevice = substationDeviceService.getById(deviceId); + if (substationDevice != null) { + map.put("lowerValue", substationDevice.getLowerValue()); + map.put("upperValue", substationDevice.getUpperValue()); + } + map.put("deviceList", deviceList); + return map; + } + + /********************************** + * 用途说明: 执行一件顺控任务 + * 参数说明 devicelist + * 返回值说明: void + ***********************************/ + @Override + public void executeTask(List> devicelist, TaskTodo quartzJob, String linkageDeviceType) throws InterruptedException { + String jobName = quartzJob.getTaskName(); + int min = Math.min(devicelist.size(), 3); + for (int i = 0; i < min; i++) { + Map map = devicelist.get(i); + Thread.sleep(1000); + int finalI = i; + new Thread(() -> { + try { + String flag = map.get("flag").toString(); + if (!("0".equals(flag) || "4".equals(flag))) { + return; + } + //排除当前时刻需要检修的巡视点 + int count = this.queryExaminePlan(map.get("deviceId").toString(), + quartzJob.getPlanStartTime()); + if (count > 0) { + //更新当前记录状态,修改为 3:设备检修中 + this.updateTaskResultStatus(quartzJob.getTaskTodoId(), + map.get("resultId").toString(), + "", "3"); + return;//跳过设备检修区间对应的巡视点 + } + //获取typelist 识别类型 + String pictureAnalysisTypeList = map.get("pictureAnalysisTypeList").toString(); + JSONArray typelist = new JSONArray(); + + if (StrUtil.isNotEmpty(pictureAnalysisTypeList)) { + com.alibaba.fastjson.JSONObject jobj = JSON.parseObject(pictureAnalysisTypeList); + + if (ObjectUtil.isNotEmpty(jobj.getString("PictureAnalysisType"))) { + if (jobj.getString("PictureAnalysisType").equals("meter")) { + typelist.add(map.get("outsideFeature").toString()); + } else { + typelist.add(jobj.getString("PictureAnalysisType")); + } + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDefectAnalysisType"))) { + typelist.add(jobj.getString("PictureDefectAnalysisType")); + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDiscriminateAnalysisType"))) { + typelist.add(jobj.getString("PictureDiscriminateAnalysisType")); + } + } + if (typelist.size() == 0) { + throw new Exception("未设置识别方式,不能进行图形识别!"); + } + + //路径命名格式为:变电站编码/年/月/日/巡视任务编码/CCD + //其中 CCD 为可见光图片 FIR 红外图谱文件 Audio 音频文件 Video 视频文件 + //图片类型 + String filetype = "CCD"; + if (ObjectUtil.isNotEmpty(map.get("fileType"))) { + switch (map.get("fileType").toString()) { + case "1": + filetype = "FIR"; + break; + case "2": + filetype = "CCD"; + break; + case "3": + filetype = "Audio"; + break; + case "4": + filetype = "Video"; + break; + default: + filetype = "CCD"; + break; + } + } + String filepath = String.format("%s/%s/%s/%s/%s/%s/", + quartzJob.getStationCode(), + DateUtil.format(DateUtil.date(), "yyyy"), + DateUtil.format(DateUtil.date(), "MM"), + DateUtil.format(DateUtil.date(), "dd"), + quartzJob.getTaskCode(), + filetype + ); + + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg +// String filename = String.format("%s_%s_%s_%s_%s_%s.mp4", +// DateUtil.format(DateUtil.date(), "yyyyMMdd"), +// DateUtil.format(DateUtil.date(), "HHmmss"), +// map.get("bayName").toString(), +// map.get("mainDeviceName").toString(), +// map.get("deviceName").toString(), +// "视频" +// ); + String filename = String.format("%s_%s_%s.mp4", + map.get("deviceId").toString(), + map.get("patroldeviceCode").toString(), + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ); + + String fullfilename = filepath + filename; + String rtspurl = String.format("rtsp://%s:%s/rtp/%s_%s", httpServerConfig.getMediaIp(), + httpServerConfig.getMonitorRtspPort(), + map.get("rtsp_device_code").toString(), map.get("rtsp_channel_code").toString()); + //设备rstp流地址不为空,优先取摄像头地址 + if (ObjUtil.isNotEmpty(map.get("rtsp_device_url"))) { + rtspurl = map.get("rtsp_device_url").toString(); + } + + com.alibaba.fastjson.JSONObject callresult; + com.alibaba.fastjson.JSONObject customParams = new com.alibaba.fastjson.JSONObject(); + typelist.clear(); + typelist.add("yjsk"); + log.info("调用摄像机截取视频"); + String videoStream = map.get("rtsp_device_code").toString() + "_" + map.get( + "rtsp_channel_code").toString(); + log.info("------录制视频------"); + String finalRtspurl = rtspurl; + String finalFullfilename = fullfilename; + httpUtil.getMonitorVideo(httpServerConfig.getffmpegPath(), finalRtspurl, finalFullfilename, + linkageDeviceType, videoStream); + //更新当前记录状态,修改为 1:已巡视(图像已采集) + fullfilename = URLEncoder.encode(fullfilename, "utf-8");//转码存储 + this.updateTaskResultStatus(quartzJob.getTaskTodoId(), map.get("resultId").toString(), + fullfilename, "1"); + + // 3、 调用图片识别算法,进行图形识别 + //表计类型 + if (ObjectUtil.isNotEmpty(map.get("meterType"))) { + customParams.put("meterType", map.get("meterType").toString()); + } + if (ObjectUtil.isNotEmpty(map.get("outsideAngle"))) { + customParams.put("TempAngleList", map.get("outsideAngle").toString()); + } + //缺陷识别结果类型 + if (ObjUtil.isNotEmpty(map.get("defectresulttypes"))) { + customParams.put("defectresulttypes", map.get("defectresulttypes").toString()); + } + + //基准图 + if (ObjUtil.isNotEmpty(map.get("patroldeviceBaseimage"))) { + customParams.put("imageNormalUrlPath", map.get("patroldeviceBaseimage").toString()); + } + //有效识别区域 + if (ObjUtil.isNotEmpty(map.get("patroldeviceEffectiveregion"))) { + customParams.put("effectiveregion", map.get("patroldeviceEffectiveregion").toString()); + } + + log.info("customParams:" + JSONUtil.toJsonStr(customParams)); + callresult = httpUtil.callPicAnalyse(map.get("resultId").toString(), + map.get("deviceId").toString(), JSONUtil.toJsonStr(typelist), + JSONUtil.toJsonStr(customParams), fullfilename); + + // 解除摄像头的工作状态 + this.updatePatrolDeviceState(map.get("stationCode").toString(), + map.get("rtsp_device_code").toString(), "0"); + + if (callresult != null) { + String code = callresult.get("code").toString(); + if ("-2".equals(code) || "201".equals(code)) { + // 设置请求分析主机调用失败的任务为巡视失败 + this.setTaskResultAndTodo(quartzJob.getStationCode(), + map.get("resultId").toString(), quartzJob.getDeviceSumnum(), + quartzJob.getTaskTodoId()); + } + } + + } catch (Exception e) { + log.error(String.format("单条任务执行失败,任务名称:%s,时间:%s", jobName, DateUtil.now())); + log.error(e.getMessage());; + // 解除摄像头的工作状态 + this.updatePatrolDeviceState(map.get("stationCode").toString(), + map.get("rtsp_device_code").toString(), "0"); + String flag = "4"; + if ("获取摄像头截图失败".equals(e.getMessage())) { + flag = "6"; + } + this.updateTaskResultStatus(quartzJob.getTaskTodoId(), map.get("resultId").toString(), + "", flag); + } + }).start(); + //摄像头视频流采集图片 + } + } + + @Override + public ResponseResult getThreadTask(String taskTodoId) { + List> deviceList = + taskResultMapper.selectMaps(new LambdaQueryWrapper().eq(TaskResult::getTaskTodoId, + taskTodoId)); + Map>> groupedByCode = + deviceList.stream().collect(Collectors.groupingBy(r -> r.get("patroldeviceCode").toString())); + // 将分组结果分配到10个新的集合中 + List>> collections = + GroupAndProcessParallel.distributeToTenCollections1(groupedByCode); + Map map = new HashMap<>(); + for (int i = 0; i < collections.size(); i++) { + List> mapList = collections.get(i); + map.put("集合" + i, mapList.size() + ""); + } + log.info(map.toString()); + return ResponseResult.success(); + } + + @Override + public void sendTaskResultById(String id) { + TaskResult taskResult = taskResultMapper.selectById(id); + TaskTodo taskTodo = this.getById(taskResult.getTaskTodoId()); + this.sendTaskResult(taskTodo, taskResult); + } + + @Override + public boolean onceAnalyse(String id) throws Exception { + SubstationDevice substationDevice = substationDeviceService.getById(id); + String pictureAnalysisTypeList = substationDevice.getPictureAnalysisTypeList(); + JSONArray typelist = new JSONArray(); + + com.alibaba.fastjson.JSONObject customParams = new com.alibaba.fastjson.JSONObject(); + if (StrUtil.isNotEmpty(pictureAnalysisTypeList)) { + com.alibaba.fastjson.JSONObject jobj = JSON.parseObject(pictureAnalysisTypeList); + if (ObjectUtil.isNotEmpty(jobj.getString("PictureAnalysisType"))) { + if ("meter".equals(jobj.getString("PictureAnalysisType"))) { + customParams.put("meterType", substationDevice.getOutsideFeature()); + } + typelist.add(jobj.getString("PictureAnalysisType")); + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDefectAnalysisType"))) { + JSONArray defectresult = new JSONArray(); + List pictureType = StrUtil.split(jobj.getString( + "PictureDefectAnalysisType"), ","); + List> pictureDefectAnalysisType = + sysDictionaryItemsService.getDeviceByType("PictureDefectAnalysisType"); + // 获取到子项 + List itemCodeList = + pictureDefectAnalysisType.stream().filter(p -> pictureType.contains(p.get("itemcode").toString())).map(m -> m.get("custom1").toString()).collect(Collectors.toList()); + + for (String code : itemCodeList) { + JSONArray jsonArray = JSONUtil.parseArray(code); + defectresult.addAll(jsonArray); + List keyList = + jsonArray.stream().map(j -> JSONUtil.parseObj(j).getStr("key")).collect(Collectors.toList()); + typelist.addAll(keyList); + } + if (defectresult.size() > 0) { + //缺陷识别结果类型 + customParams.put("defectresulttypes", defectresult); + } + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDiscriminateAnalysisType"))) { + typelist.add(jobj.getString("PictureDiscriminateAnalysisType")); + } + } + + if (typelist.size() == 0) { + throw new Exception("未设置识别方式,不能进行图形识别!"); + } + + String filetype = "CCD"; + if (ObjectUtil.isNotEmpty(substationDevice.getSaveTypeList())) { + switch (substationDevice.getSaveTypeList()) { + case "1": + filetype = "FIR"; + break; + case "3": + filetype = "Audio"; + break; + case "4": + filetype = "Video"; + break; + default: + filetype = "CCD"; + break; + } + } + String filepath = String.format("%s/%s/%s/%s/%s/%s/", + substationDevice.getStationCode(), + DateUtil.format(DateUtil.date(), "yyyy"), + DateUtil.format(DateUtil.date(), "MM"), + DateUtil.format(DateUtil.date(), "dd"), + substationDevice.getDeviceName(), + filetype + ); + + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg +// String filename = String.format("%s_%s_%s_%s_%s_%s.jpg", +// DateUtil.format(DateUtil.date(), "yyyyMMdd"), +// DateUtil.format(DateUtil.date(), "HHmmss"), +// substationDevice.getBayName(), +// substationDevice.getMainDeviceName(), +// substationDevice.getDeviceName(), +// "原图" +// ); + String patroldeviceJson = substationDevice.getPatroldeviceJson(); + if (StrUtil.isBlank(patroldeviceJson)) { + throw new RuntimeException("当前点位未绑定摄像机"); + } + if (!JSONUtil.isTypeJSONArray(patroldeviceJson)) { + throw new RuntimeException("当前点位未绑定摄像机"); + } + JSONArray patroldeviceJsonArray = JSONUtil.parseArray(patroldeviceJson); + if (patroldeviceJsonArray.size() <= 0) { + throw new RuntimeException("当前点位未绑定摄像机"); + } + // 取出点位的第一个摄像机 + JSONObject patroldeviceJsonObject = patroldeviceJsonArray.getJSONObject(0); + // 视频播放编号 + String patroldeviceCode = patroldeviceJsonObject.getStr("patroldevice_code"); + // 视频播放通道 + String patroldeviceChannelcode = patroldeviceJsonObject.getStr("patroldevice_channelcode"); + String filename = String.format("%s_%s_%s.jpg", + substationDevice.getDeviceId(), + patroldeviceCode, + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ); + + String fullfilename = filepath + filename; + String csvFilePath = httpServerConfig.getSnapFilePath() + filepath + filename; + // 视频播放预置位 + String patroldevicePos = patroldeviceJsonObject.getStr("patroldevice_pos"); + String rtspurl = String.format("rtsp://%s:%s/rtp/%s_%s", httpServerConfig.getMediaIp(), + httpServerConfig.getMonitorRtspPort(), patroldeviceCode, patroldeviceChannelcode); + JSONObject map = new JSONObject(); + + map.putOpt("deviceId", substationDevice.getDeviceId()); + map.putOpt("valid", "0"); + map.putOpt("meterType", substationDevice.getMeterType()); + map.putOpt("recognitionType", substationDevice.getRecognitionTypeList()); + map.putOpt("value", ""); + try { + //图像检测 + if ("1".equals(httpServerConfig.getFilefromtype())) { + //锁定摄像机状态(设置摄像机为工作状态,其他地方不能调用预置位操作) + this.updatePatrolDeviceState(substationDevice.getStationCode(), patroldeviceCode, "1"); + + log.info("开始调用摄像头预置位"); + com.alibaba.fastjson.JSONObject callresult1 = httpUtil.callDevicePos(patroldeviceCode, + patroldeviceChannelcode, patroldevicePos, null); + log.info("摄像头预置位调用成功!" + JSONUtil.toJsonStr(callresult1)); + //延迟时间 + Thread.sleep(8000); + log.info("开始调用视频并截图"); + log.info("rtspurl" + rtspurl); + httpUtil.getMonitorVideoSnap(httpServerConfig.getffmpegPath(), rtspurl, fullfilename); + log.info("完成视频截图并保存到对应位置下!,文件名称:" + fullfilename); + } + } catch (Exception e) { + map.putOpt("desc", "采集失败"); + map.putOpt("analysisResult", "采集失败"); + map.putOpt("filePath", ""); + map.putOpt("defectFilePath", ""); + WebSocketServer.sendInfo("deviceresult_" + substationDevice.getStationCode(), map.toString()); + } + //更新当前记录状态,修改为 1:已巡视(图像已采集) + //转码存储 + String encodefilename = fullfilename; + fullfilename = URLEncoder.encode(fullfilename, "utf-8"); + if (ObjectUtil.isNotEmpty(substationDevice.getOutsideAngle())) { + if (JSONUtil.isTypeJSONArray(substationDevice.getOutsideAngle())) { + JSONArray jsonArray = JSONUtil.parseArray(substationDevice.getOutsideAngle()); + customParams.put("TempAngleList", jsonArray); + } + } + + //有效识别区域 + if (ObjUtil.isNotEmpty(patroldeviceJsonObject.getStr("effective_region"))) { + customParams.put("effectiveregion", patroldeviceJsonObject.getStr("effective_region")); + } + + log.info("==============================typelist=======================" + typelist.toString()); + com.alibaba.fastjson.JSONObject callresult = null; + String resultId = substationDevice.getDeviceId(); + String objectId = substationDevice.getDeviceId(); + if ("infrared".equals(typelist.get(0).toString())) { + //红外测温 + log.info("进入红外测温方法"); + String outsideAngle = ""; + if (ObjectUtil.isNotEmpty(substationDevice.getOutsideAngle())) { + outsideAngle = substationDevice.getOutsideAngle(); + } + // 调用温度的sdk识别并给图片加上温度 + callresult = this.callPicAnalyse(resultId, objectId, patroldeviceCode, rtspurl, fullfilename, + outsideAngle, csvFilePath); + } else { + callresult = httpUtil.callPicAnalyse(resultId, objectId, JSONUtil.toJsonStr(typelist), + JSONUtil.toJsonStr(customParams), encodefilename); + } + // 解除摄像头的工作状态 + this.updatePatrolDeviceState(substationDevice.getStationCode(), patroldeviceCode, "0"); + + if (callresult != null) { + String code = callresult.get("code").toString(); + if ("-2".equals(code) || "201".equals(code)) { + map.putOpt("flag", "4"); + String fileName = URLEncoder.encode(encodefilename, "utf-8"); + map.putOpt("filePath", fileName); + map.putOpt("defectFilePath", fileName); + map.putOpt("desc", "巡视失败"); + map.putOpt("analysisResult", "识别失败"); + WebSocketServer.sendInfo("deviceresult_" + substationDevice.getStationCode(), map.toString()); + } + } + return true; + } + + @Override + public void exportMeterDataByTaskTodoId(String taskTodoId, HttpServletResponse response) { + List> mapList = taskResultMapper.selectMaps(new LambdaQueryWrapper().eq(TaskResult::getTaskTodoId, taskTodoId).eq(TaskResult::getRecognitionType,"1")); + List> recognition_type = sysDictionaryItemsService.getDeviceByType("recognition_type"); + List> DeviceClass = sysDictionaryItemsService.getDeviceByType("DeviceClass"); + // 表计类型 + List> meterTypeList = sysDictionaryItemsService.getDeviceByType("MeterType"); + mapList.forEach(record->{ + String deviceClass = ""; + if (!Objects.isNull(record.get("deviceClass"))) { + Map map = + DeviceClass.stream().filter(r -> !Objects.isNull(record.get("deviceClass")) && r.get( + "itemcode").equals(record.get("deviceClass").toString())).findFirst().orElse(null); + if (map != null) { + deviceClass = map.get("dictname").toString(); + } + } + record.put("deviceClassName", deviceClass); + String meterType=""; + if (!Objects.isNull(record.get("meterType"))) { + Map map = meterTypeList.stream().filter(r -> !Objects.isNull(record.get("meterType")) && r.get( + "itemcode").equals(record.get("meterType").toString())).findFirst().orElse(null); + if (map != null) { + meterType = map.get("dictname").toString(); + } + } + record.put("meterType", meterType); + String recognitionType = ""; + if (!Objects.isNull(record.get("recognitionType"))) { + Map map = recognition_type.stream().filter(r -> !Objects.isNull(record.get( + "recognitionType")) && r.get("itemcode").equals(record.get("recognitionType").toString())).findFirst().orElse(null); + if (map != null) { + recognitionType = map.get("dictname").toString(); + } + } + record.put("recognitionTypeName", recognitionType); + if (ObjectUtil.isNotEmpty(record.get("filePath"))) { + String filePath = record.get("filePath").toString(); + try { + filePath = URLDecoder.decode(filePath, "utf-8"); + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage());; + } + record.put("img", httpServerConfig.getSnapFilePath() + filePath); + + } + if (ObjectUtil.isNotEmpty(record.get("value"))) { + String value = record.get("value").toString(); + String unit = ObjectUtil.isNotEmpty(record.get("unit")) ? record.get("unit").toString() : ""; + record.put("value", value + unit); + } + }); + + String[] headers = {"区域名称", "间隔名称", "设备名称", "部件名称", "点位名称", "点位分类", "识别类型","表计类型", "巡视结果", "巡视结论", "巡视时间", "巡视图片",}; + String[] keys = {"areaName", "bayName", "mainDeviceName", "componentName", "deviceName", "deviceClassName", "recognitionTypeName", "meterType","value", "analysisResult", "time", "img"}; + String imgs = "img"; + try { + FileUtil.excelImg(mapList, headers, keys, imgs, "", response); + } catch (IOException e) { + log.error(e.getMessage());; + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TodoTaskJob.java b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TodoTaskJob.java new file mode 100644 index 0000000..3b4670b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/patroltask/service/impl/TodoTaskJob.java @@ -0,0 +1,721 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.modules.patroltask.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.SubstationPatroldeviceMapper; +import com.yfd.platform.modules.basedata.service.ILinkageSignalService; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.patroltask.domain.TaskDolog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.service.ITaskService; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.GroupAndProcessParallel; +import com.yfd.platform.utils.HttpRESTfulUtils; +import com.yfd.platform.utils.SpringContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.quartz.InterruptableJob; +import org.quartz.JobExecutionContext; +import org.quartz.UnableToInterruptJobException; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.io.File; +import java.net.URLEncoder; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * @date 2023-04-28 + * 同步执行任务,避免资源冲突 + */ +@Slf4j +public class TodoTaskJob extends QuartzJobBean implements InterruptableJob { + + private boolean _interrupted = false; // job 是否中断 + + @Override + public void executeInternal(JobExecutionContext context) { + + TaskTodo quartzJob = + (TaskTodo) context.getMergedJobDataMap().get(TaskTodo.JOB_KEY); + // 获取spring bean + ITaskTodoService todoTaskService = + SpringContextHolder.getBean(ITaskTodoService.class); + // 获取spring bean + ITaskService taskService = SpringContextHolder.getBean(ITaskService.class); + // 获取spring bean 中 HttpRESTfulUtils 类实例 + HttpRESTfulUtils httpUtil = + SpringContextHolder.getBean(HttpRESTfulUtils.class); + // 获取spring bean 中 ILinkageSignalService 类实例 + ILinkageSignalService linkageSignalService = + SpringContextHolder.getBean(ILinkageSignalService.class); + ISubstationDeviceService substationDeviceService = SpringContextHolder.getBean(ISubstationDeviceService.class); + SubstationPatroldeviceMapper substationPatroldeviceMapper = + SpringContextHolder.getBean(SubstationPatroldeviceMapper.class); + // 获取spring bean 中 HttpServerConfig 类实例 + HttpServerConfig config = + SpringContextHolder.getBean(HttpServerConfig.class); + ISysDictionaryItemsService sysDictionaryItemsService = + SpringContextHolder.getBean(ISysDictionaryItemsService.class); + TaskResultMapper taskResultMapper = SpringContextHolder.getBean(TaskResultMapper.class); +// QuartzMultiTaskManage quartzMultiTaskManage = SpringContextHolder.getBean(QuartzMultiTaskManage.class); + + if ("false".equals(config.getDoTask())) { + return; + } + TaskTodo taskTodo = todoTaskService.getById(quartzJob.getTaskTodoId()); + if ("1".equals(quartzJob.getTaskState()) || "3".equals(quartzJob.getTaskState())) { + return; + } + if (taskTodo == null) { + return; + } + String dockInfo = taskTodo.getDockInfo(); + if (StrUtil.isNotBlank(dockInfo) && JSONUtil.isTypeJSONObject(dockInfo)) { + // 发送机巢任务执行 + String api = "wayline/api/v1/myWayLine/flight-tasks-execute"; + if (StrUtil.isNotBlank(config.getDockServerApp())) { + api = config.getDockServerApp() + "/" + api; + } + Map param = new HashMap<>(); + param.put("jobId", taskTodo.getTaskTodoId()); + JSONObject jsonObject = httpUtil.sendHttpPost("json", config.getDockServerIp(), + config.getDockServerPort(), "", api, param, null); + if (!SystemCode.SUCCESS_STATUS_CODE.getCode().equals(jsonObject.getString("code"))) { + throw new RuntimeException(jsonObject.getString("msg")); + } + return; + } + + long startTime = System.currentTimeMillis(); + String jobName = quartzJob.getTaskName(); +// int waitTime = 20000; + int waitTime = 3000; + try { + // 执行任务 + log.info( + "--------------------------------------------------------------------------------------------------------------"); + log.info(String.format("任务开始执行[%s];任务名称:%s;任务ID:%s;计划执行时间:%s", DateUtil.now(), jobName, + quartzJob.getTaskTodoId(), quartzJob.getPlanStartTime())); + // 任务增加开始时间 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + String startTime1 = quartzJob.getStartTime(); + if (StrUtil.isBlank(startTime1)) { + updateWrapper.eq(TaskTodo::getTaskTodoId, quartzJob.getTaskTodoId()).ne(TaskTodo::getTaskState, "1").set(TaskTodo::getTaskState, "2").set(TaskTodo::getStartTime, DateUtil.now()); + todoTaskService.update(updateWrapper); + if (StrUtil.isBlank(quartzJob.getControlNum())) { + WebSocketServer.sendInfo("taskresult_" + quartzJob.getStationCode(), "msg"); + } else { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("taskTodoId", taskTodo.getTaskTodoId()); + jsonObject.put("taskType", taskTodo.getTaskType()); + jsonObject.put("status", "1"); + WebSocketServer.sendInfo("taskresult_" + taskTodo.getStationCode(), jsonObject.toString()); + try { + Thread.sleep(2000); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + } + String priority = "2".equals(quartzJob.getCustom3()) ? "4" : quartzJob.getPriority(); + //0、任务开始前,先暂停当前执行中的比当前任务级别低的任务 + todoTaskService.pauseLowerTask(quartzJob.getStationCode(), quartzJob.getTaskTodoId(), priority); + // 任务开始前,判断是否有任务等级大于等于当前任务的,有就暂停当前任务 + if (StrUtil.isBlank(quartzJob.getCustom3())) { + todoTaskService.pauseUpperTask(quartzJob.getStationCode(), quartzJob.getTaskTodoId(), + quartzJob.getPriority()); + } + // 1、 根据任务ID 查询 当前任务需要巡视的点位 + List> devicelist = todoTaskService.queryTaskResult(quartzJob.getTaskTodoId()); + List> deviceList = new ArrayList<>(); + String linkageType = null; + String linkageDeviceType = null; + if (StrUtil.isNotBlank(taskTodo.getControlNum())) { + LinkageSignal linkageSignal = + linkageSignalService.getOne(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, taskTodo.getControlNum())); + linkageType = linkageSignal.getLinkageType(); + linkageDeviceType = linkageSignal.getLinkageDeviceType(); + if ("1".equals(linkageSignal.getLinkageType())) { + List> collect = devicelist.stream().filter(d -> !d.get("deviceName").toString().contains("全景")).collect(Collectors.toList()); + deviceList.addAll(collect); + // todoTaskService.executeTask(devicelist,quartzJob, linkageDeviceType); + } else { + deviceList.addAll(devicelist); + } + // deviceList.addAll(devicelist); + + } else { + deviceList.addAll(devicelist); + } + // 2、 遍历巡视点位对应摄像头位置信息 + AtomicBoolean isFlag = new AtomicBoolean(false); + Map>> groupedByCode = + deviceList.stream().collect(Collectors.groupingBy(r -> r.get("patroldeviceCode").toString())); + // 将分组结果分配到n个新的集合中 + List>> collections = + GroupAndProcessParallel.distributeToTenCollections1(groupedByCode); + // 创建一个固定大小的线程池 + ExecutorService executorService = Executors.newFixedThreadPool(6); + AtomicInteger number = new AtomicInteger(); + int size = 0; + // 提交任务到线程池 + for (final List> maps : collections) { + String finalLinkageType = linkageType; + String finalLinkageDeviceType = linkageDeviceType; + size += maps.size(); + log.info("数量" + size); + executorService.submit(() -> { + // 集合内部单线程处理 + //摄像头视频流采集图片 + for (Map map : maps) { + try { + if (_interrupted) { + isFlag.set(true); + //跳出执行 + return; + } + String flag = map.get("flag").toString(); + if (!("0".equals(flag) || "4".equals(flag))) { + continue; + } + number.getAndIncrement(); + //排除当前时刻需要检修的巡视点 + int count = todoTaskService.queryExaminePlan(map.get("deviceId").toString(), + quartzJob.getPlanStartTime()); + if (count > 0) { + //更新当前记录状态,修改为 3:设备检修中 + todoTaskService.updateTaskResultStatus(quartzJob.getTaskTodoId(), + map.get("resultId").toString(), "", "3"); + continue;//跳过设备检修区间对应的巡视点 + } + + //获取typelist 识别类型 + String pictureAnalysisTypeList = map.get("pictureAnalysisTypeList").toString(); + JSONArray typelist = new JSONArray(); + String analysePort = config.getAnalysePort11(); + JSONObject customParams = new JSONObject(); + customParams.put("taskName", taskTodo.getTaskName()); + if (StrUtil.isNotEmpty(pictureAnalysisTypeList)) { + JSONObject jobj = JSON.parseObject(pictureAnalysisTypeList); + + if (ObjectUtil.isNotEmpty(jobj.getString("PictureAnalysisType"))) { + if ("meter".equals(jobj.getString("PictureAnalysisType"))) { + customParams.put("meterType", map.get("outsideFeature").toString()); + } + typelist.add(jobj.getString("PictureAnalysisType")); + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDefectAnalysisType"))) { + JSONArray defectresult = new JSONArray(); + analysePort = config.getAnalysePort12(); + List pictureType = StrUtil.split(jobj.getString( + "PictureDefectAnalysisType"), ","); + List> pictureDefectAnalysisType = + sysDictionaryItemsService.getDeviceByType("PictureDefectAnalysisType"); + // 获取到子项 + List itemCodeList = + pictureDefectAnalysisType.stream().filter(p -> pictureType.contains(p.get("itemcode").toString())).map(m -> m.get("custom1").toString()).collect(Collectors.toList()); + + for (String code : itemCodeList) { + JSONArray jsonArray = JSONUtil.parseArray(code); + defectresult.addAll(jsonArray); + List keyList = + jsonArray.stream().map(j -> JSONUtil.parseObj(j).getStr("key")).collect(Collectors.toList()); + typelist.addAll(keyList); + } + if (defectresult.size() > 0) { + //缺陷识别结果类型 + customParams.put("defectresulttypes", defectresult); + } + } + if (ObjectUtil.isNotEmpty(jobj.getString("PictureDiscriminateAnalysisType"))) { + typelist.add(jobj.getString("PictureDiscriminateAnalysisType")); + analysePort = config.getAnalysePort12(); + } + } + + //缺陷识别结果类型 + // if (ObjUtil.isNotEmpty(map.get("defectresulttypes"))) { + // JSONArray jsonArray = JSONUtil.parseArray(map.get + // ("defectresulttypes").toString()); + // customParams.put("defectresulttypes", jsonArray); + // } + + if (typelist.size() == 0) { + throw new Exception("未设置识别方式,不能进行图形识别!"); + } + + //路径命名格式为:变电站编码/年/月/日/巡视任务编码/CCD + //其中 CCD 为可见光图片 FIR 红外图谱文件 Audio 音频文件 Video 视频文件 + //图片类型 + String filetype = "CCD"; + if (ObjectUtil.isNotEmpty(map.get("fileType"))) { + switch (map.get("fileType").toString()) { + case "1": + filetype = "FIR"; + break; + case "3": + filetype = "Audio"; + break; + case "4": + filetype = "Video"; + break; + default: + filetype = "CCD"; + break; + } + } + String filepath = String.format("%s/%s/%s/%s/%s/%s/", + quartzJob.getStationCode(), + DateUtil.format(DateUtil.date(), "yyyy"), + DateUtil.format(DateUtil.date(), "MM"), + DateUtil.format(DateUtil.date(), "dd"), + quartzJob.getTaskCode(), + filetype + ); + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg +// String filename = String.format("%s_%s_%s_%s_%s_%s.jpg", +// DateUtil.format(DateUtil.date(), "yyyyMMdd"), +// DateUtil.format(DateUtil.date(), "HHmmss"), +// map.get("bayName").toString(), +// map.get("mainDeviceName").toString(), +// map.get("deviceName").toString(), +// "原图" +// ); +// device_id patroldevice_code + String filename = String.format("%s_%s_%s.jpg", + map.get("deviceId").toString(), + map.get("patroldeviceCode").toString(), + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ); + if ("1".equals(finalLinkageType)) { + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg +// filename = String.format("%s_%s_%s_%s_%s_%s.mp4", +// DateUtil.format(DateUtil.date(), "yyyyMMdd"), +// DateUtil.format(DateUtil.date(), "HHmmss"), +// map.get("bayName").toString(), +// map.get("mainDeviceName").toString(), +// map.get("deviceName").toString(), +// "视频" +// ); + filename = String.format("%s_%s_%s.mp4", + map.get("deviceId").toString(), + map.get("patroldeviceCode").toString(), + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ); + } + String fullfilename = filepath + filename; + String csvFilePath = config.getSnapFilePath() + filepath + filename; + String rtspurl = String.format("rtsp://%s:%s/rtp/%s_%s", config.getMediaIp(), + config.getMonitorRtspPort(), + map.get("rtspDeviceCode").toString(), map.get("rtspChannelCode").toString()); + + List devices = + substationPatroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getInternationalId, map.get("patroldeviceCode").toString())); + if (devices.size() > 0) { + SubstationPatroldevice device = devices.get(0); + if (ObjUtil.isNotEmpty(device.getChannelinfo()) && JSONUtil.isTypeJSONArray(device.getChannelinfo())) { + JSONArray channels = JSONUtil.parseArray(device.getChannelinfo()); + for (int i = 0; i < channels.size(); i++) { + cn.hutool.json.JSONObject channel = channels.getJSONObject(i); + //设备rstp流地址不为空,优先取摄像头地址 + if (map.get("patroldeviceChannelcode").toString().equals(channel.getStr( + "channel_code"))) { + if (StrUtil.isNotBlank(channel.getStr("channel_rtspurl"))) { + rtspurl = channel.getStr("channel_rtspurl"); + break; + } + + } + } + } + } + List mapList = new ArrayList<>(); + JSONObject callresult = null; + if ("sound".equals(typelist.get(0).toString())) { + //声音检测 + // rtspurl = map.get("rtspDeviceUrl").toString(); + filename = String.format("%s_%s_%s_%s_%s.wav", + DateUtil.format(DateUtil.date(), "yyyyMMdd"), + DateUtil.format(DateUtil.date(), "HHmmss"), + map.get("bayName").toString(), + map.get("mainDeviceName").toString(), + map.get("deviceName").toString() + ); + fullfilename = filepath + filename; + httpUtil.getMonitorAudioSnap(config.getffmpegPath(), rtspurl, fullfilename); + //更新当前记录状态,修改为 1:已巡视(声音已采集) + fullfilename = URLEncoder.encode(fullfilename, "utf-8");//转码存储 + todoTaskService.updateTaskResultStatus(quartzJob.getTaskTodoId(), + map.get("resultId").toString(), + fullfilename, "2"); + + log.info("customParams:" + JSONUtil.toJsonStr(customParams)); + callresult = httpUtil.callPicAnalyse(map.get("taskTodoId").toString(), + map.get("resultId").toString(), JSONUtil.toJsonStr(typelist), + JSONUtil.toJsonStr(customParams), fullfilename); + + } else { + if (!"3".equals(finalLinkageType)) { + if ("1".equals(finalLinkageType)) { + typelist.clear(); + typelist.add("yjsk"); + log.info("调用摄像机截取视频"); + String videoStream = map.get("rtspDeviceCode").toString() + "_" + map.get( + "rtspChannelCode").toString(); + httpUtil.getMonitorVideo(config.getffmpegPath(), rtspurl, fullfilename, + finalLinkageDeviceType, videoStream); + if ("遥信".equals(finalLinkageDeviceType)) { + fullfilename = null; + } + } else { + //图像检测 + if ("1".equals(config.getFilefromtype())) { + //锁定摄像机状态(设置摄像机为工作状态,其他地方不能调用预置位操作) + todoTaskService.updatePatrolDeviceState(map.get("stationCode").toString() + , map.get( + "rtspDeviceCode").toString(), "1"); + + log.info("开始调用摄像头预置位"); + JSONObject callresult1 = + httpUtil.callDevicePos(map.get("rtspDeviceCode").toString(), + map.get("rtspChannelCode").toString(), map.get( + "rtspChannelPos").toString(), null); + log.info("摄像头预置位调用成功!" + JSONUtil.toJsonStr(callresult1)); + //延迟时间 + Thread.sleep(waitTime); + log.info("开始调用视频并截图"); + log.info("rtspurl" + rtspurl); + String type = null; + httpUtil.getMonitorVideoSnap(config.getffmpegPath(), rtspurl, fullfilename); + log.info("完成视频截图并保存到对应位置下!,文件名称:" + fullfilename); + String identifyMaterialId = ObjectUtil.isNotEmpty(map.get( + "identifyMaterialId")) ? map.get( + "identifyMaterialId").toString() : ""; + if ("1".equals(identifyMaterialId)) { + // 设置实物ID + todoTaskService.setMaterialIdByImg(map.get("resultId").toString(), + config.getSnapFilePath() + fullfilename); + } + } else if ("3".equals(config.getFilefromtype())) { + typelist.clear(); + String typeStr = "bj_bpmh,bj_bpps,bj_wkps,bjdsyc_zz,bjdsyc_ywj,bmwh," + + "hxq_gjbs,hxq_gjtps,hxq_yfps,jyz_pl,pzqcd,sly_dmyw,ws_ywyc," + + "xmbhyc,yljdq_flow,yljdq_stop,yw_gkxfw,ywzt_yfyc"; +// String typeStr = "sly_dmyw"; + typelist.addAll(StrUtil.split(typeStr, ",")); + //检测系统指定文件目录 + String sourceFilePath = config.getTestfilepath(); + File directory = new File(sourceFilePath); + if (directory.exists() && directory.isDirectory()) { + File[] files = directory.listFiles(); + if (files != null && files.length > 0) { + String taskTodoId = taskTodo.getTaskTodoId(); + LambdaUpdateWrapper updateWrapper1 = + new LambdaUpdateWrapper<>(); + updateWrapper1.eq(TaskTodo::getTaskTodoId, taskTodoId).set(TaskTodo::getDeviceSumnum, files.length); + todoTaskService.update(updateWrapper1); + TaskResult taskResult = taskResultMapper.selectById(map.get("resultId").toString()); + taskResultMapper.deleteById(taskResult.getResultId()); + for (int i = 0; i < files.length; i++) { + TaskResult taskResult1 = BeanUtil.copyProperties(taskResult, + TaskResult.class); + String id = IdUtil.simpleUUID(); + taskResult1.setResultId(id); + String firstFileName = files[i].getName(); + String sourceFile = sourceFilePath + firstFileName; + + String filepath1 = String.format("%s/%s/%s/%s/%s/%s/", + quartzJob.getStationCode(), + DateUtil.format(DateUtil.date(), "yyyy"), + DateUtil.format(DateUtil.date(), "MM"), + DateUtil.format(DateUtil.date(), "dd"), + quartzJob.getTaskCode(), + filetype + ); + //文件命名格式为:日期_时间_间隔名_设备名_点位名_类型.jpg + // String filename1 = String.format("%s_%s_%s_%s_%s_%s_%s.jpg", + // DateUtil.format(DateUtil.date(), "yyyyMMdd"), + // DateUtil.format(DateUtil.date(), "HHmmss"), + // map.get("bayName").toString(), + // map.get("mainDeviceName").toString(), + // map.get("deviceName").toString(), + // i, + // "原图" + // ); + String filename1 = firstFileName.replace(".", "_原图."); + String fullfilename1 = filepath1 + filename1; + FileUtil.touch(config.getSnapFilePath() + fullfilename1); + FileUtil.copy(sourceFile, + config.getSnapFilePath() + fullfilename1, true); + // FileUtil + // .del + // (sourceFile); + taskResult1.setFilePath(fullfilename1); + taskResultMapper.insert(taskResult1); + mapList.add(taskResult1); + } + + } else { + log.error("目录为空或没有文件"); + } + } else { + log.error("指定的路径不是一个有效的目录"); + } + } + } + } + //更新当前记录状态,修改为 1:已巡视(图像已采集) + //转码存储 + String encodefilename = fullfilename; + fullfilename = URLEncoder.encode(fullfilename, "utf-8"); + if (!"3".equals(config.getFilefromtype())) { + todoTaskService.updateTaskResultStatus(quartzJob.getTaskTodoId(), map.get("resultId").toString(), fullfilename, "1"); + } + // 3、 调用图片识别算法,进行图形识别 + //表计类型 + // if (ObjectUtil.isNotEmpty(map.get("meterType"))) { + // customParams.put("meterType", map.get + // ("meterType").toString()); + // } + if (ObjectUtil.isNotEmpty(map.get("outsideAngle"))) { + if (JSONUtil.isTypeJSONArray(map.get("outsideAngle").toString())) { + JSONArray jsonArray = JSONUtil.parseArray(map.get("outsideAngle").toString()); + customParams.put("TempAngleList", jsonArray); + } + + } + + //基准图 + if (ObjUtil.isNotEmpty(map.get("patroldeviceBaseimage"))) { + String path = URLEncoder.encode(map.get("patroldeviceBaseimage").toString(), "utf-8"); + String imageurl = String.format("http://%s:%s/%s/snapNormalFile?filename=%s", + config.getPatrolIp(), config.getPatrolPort(), config.getPatrolAppname(), + path); + customParams.put("imageNormalUrlPath", imageurl); + } + + //有效识别区域 + if (ObjectUtil.isNotEmpty(map.get("patroldeviceJson"))) { + String pos = map.get("patroldevicePos").toString(); + String channelcode = map.get("patroldeviceChannelcode").toString(); + JSONArray patroldevices = + JSONUtil.parseArray(map.get("patroldeviceJson").toString()); + for (int i = 0; i < patroldevices.size(); i++) { + cn.hutool.json.JSONObject patroldevice = patroldevices.getJSONObject(i); + if (pos.equals(patroldevice.getStr("patroldevice_pos")) && channelcode.equals(patroldevice.getStr("patroldevice_channelcode"))) { + if (ObjUtil.isNotEmpty(patroldevice.getStr("effective_region"))) { + customParams.put("effectiveregion", patroldevice.getStr( + "effective_region")); + } + } + + } + } + + //有效识别区域 + // if (ObjUtil.isNotEmpty(map.get + // ("patroldeviceEffectiveregion"))) { + // customParams.put("effectiveregion", map.get + // ("patroldeviceEffectiveregion").toString()); + // } + log.info("==============================typelist=======================" + typelist.toString()); + if ("infrared".equals(typelist.get(0).toString())) { + //红外测温 + log.info("进入红外测温方法"); + String outsideAngle = ""; + if (ObjectUtil.isNotEmpty(map.get("outsideAngle"))) { + outsideAngle = map.get("outsideAngle").toString(); + } + String resultId = map.get("taskTodoId").toString(); + String objectId = map.get("resultId").toString(); + String patrolDeviceCode = map.get("rtspDeviceCode").toString(); + // 调用温度的sdk识别并给图片加上温度 + callresult = todoTaskService.callPicAnalyse(resultId, objectId, patrolDeviceCode, + rtspurl, fullfilename, outsideAngle, + csvFilePath); + } else { + + if ("3".equals(config.getFilefromtype())) { +// for (TaskResult taskResult : mapList) { +// Thread.sleep(3000); +// httpUtil.callPicAnalyse(map.get("taskTodoId").toString(), taskResult.getResultId(), JSONUtil.toJsonStr(typelist), JSONUtil.toJsonStr(customParams), taskResult.getFilePath()); +// } + // 线程池大小 + int numberOfThreads = 1; + + // 计算每个线程需要处理的数据量 + int chunkSize = mapList.size() / numberOfThreads; + // 线程池 + ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); + + // 提交任务给线程池 + for (int i = 0; i < numberOfThreads; i++) { + int start = i * chunkSize; + int end = (i == numberOfThreads - 1) ? mapList.size() : start + chunkSize; + List subList = mapList.subList(start, end); + + // 提交一个Callable或Runnable任务给线程池 + // 这里我们使用Runnable和一个匿名内部类来简化示例 + executor.submit(() -> { + for (TaskResult taskResult : subList) { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + log.error(e.getMessage());; + } + log.info("customParams:" + JSONUtil.toJsonStr(customParams)); + try { + httpUtil.callPicAnalyse(map.get("taskTodoId").toString(), taskResult.getResultId(), JSONUtil.toJsonStr(typelist), + JSONUtil.toJsonStr(customParams), taskResult.getFilePath()); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + }); + } + + } else { + callresult = httpUtil.callPicAnalyse(map.get("taskTodoId").toString(), + map.get("resultId").toString(), JSONUtil.toJsonStr(typelist), + JSONUtil.toJsonStr(customParams), encodefilename); + } + } + + // 解除摄像头的工作状态 + todoTaskService.updatePatrolDeviceState(map.get("stationCode").toString(), map.get( + "rtspDeviceCode").toString(), "0"); + } + + if (callresult != null) { + String code = callresult.get("code").toString(); + if ("-2".equals(code) || "201".equals(code)) { + // 设置请求分析主机调用失败的任务为巡视失败 + todoTaskService.setTaskResultAndTodo(quartzJob.getStationCode(), + map.get("resultId").toString(), quartzJob.getDeviceSumnum(), + quartzJob.getTaskTodoId()); + } + } + + } catch (Exception e) { + log.error(String.format("单条任务执行失败,任务名称:%s,时间:%s", jobName, DateUtil.now())); + log.error(e.getMessage());; + try { + // 解除摄像头的工作状态 + todoTaskService.updatePatrolDeviceState(map.get("stationCode").toString(), map.get( + "rtspDeviceCode").toString(), "0"); + String flag = "4"; + if ("获取摄像头截图失败".equals(e.getMessage())) { + flag = "6"; + } + todoTaskService.updateTaskResultStatus(quartzJob.getTaskTodoId(), map.get("resultId").toString(), "", flag); + } catch (Exception e1) { + log.error("====================单条任务失败================"); + log.error(e1.getMessage()); + } + + } + + } + + }); + } + + // 关闭线程池并等待所有任务完成 + executorService.shutdown(); + // 等待所有任务完成,这里不设置超时时间,也可以根据需要设置超时时间 + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); + //9 当前巡视任务完成后,恢复暂停的任务 + if (!isFlag.get()) { + todoTaskService.resumeLowerTask(quartzJob.getStationCode(), quartzJob.getTaskTodoId(), + quartzJob.getPriority(), quartzJob.getCustom3()); + } + + // 更新任务进度 + todoTaskService.update(new LambdaUpdateWrapper().eq(TaskTodo::getTaskTodoId, taskTodo.getTaskTodoId()).set(TaskTodo::getTaskProgress, "100").set(TaskTodo::getTaskState, "1").set(TaskTodo::getEndTime, DateUtil.now())); + // 更新任务进度 + taskResultMapper.update(null, new LambdaUpdateWrapper().eq(TaskResult::getTaskTodoId, taskTodo.getTaskTodoId()).eq(TaskResult::getFlag, "0").set(TaskResult::getFlag, "1")); + log.info("=======================执行点位数量=========================" + number); + // 任务执行完判断如果是周期任务看是否还存在没生成完的任务,接着后面生成一条 + taskService.createOnceTodoTask(taskTodo.getTaskId()); + long endTime = System.currentTimeMillis(); + long times = endTime - startTime; + // 10-记录任务执行日志 + TaskDolog taskDolog = new TaskDolog(); + taskDolog.setId(IdUtil.fastSimpleUUID()); + taskDolog.setStationCode(quartzJob.getStationCode()); + taskDolog.setTaskCode(quartzJob.getTaskCode()); + taskDolog.setTaskName(quartzJob.getTaskName()); + taskDolog.setTaskTodoid(quartzJob.getTaskTodoId()); + if (_interrupted) { + taskDolog.setDoType("系统中断任务"); + } else { + taskDolog.setDoType("系统执行任务"); + } + taskDolog.setPlanStartTime(quartzJob.getPlanStartTime()); + taskDolog.setDoStartTime(DateUtil.formatDateTime(DateUtil.date(startTime))); + taskDolog.setDoTotalTime(String.format("%.1f", times * 1.0 / 1000)); + taskDolog.setLogger(quartzJob.getLastmodifier()); + taskDolog.setLogTime(new Timestamp(DateUtil.current())); + taskDolog.setDescription(String.format("任务执行完毕[%s];共耗时:%s秒;任务名称:%s;任务ID:%s", DateUtil.now(), + String.format("%.1f", times * 1.0 / 1000), jobName, quartzJob.getTaskTodoId())); + todoTaskService.recordTaskDoLog(taskDolog); + + log.info(String.format("任务执行完毕[%s];共耗时:%s秒;任务名称:%s;任务ID:%s", DateUtil.now(), String.format("%" + + ".1f", times * 1.0 / 1000), jobName, quartzJob.getTaskTodoId())); + log.info( + "--------------------------------------------------------------------------------------------------------------"); + } catch (Exception e) { + log.error(String.format("任务执行失败[%s];任务名称:%s;任务ID:%s;", DateUtil.now(), jobName, + quartzJob.getTaskTodoId())); + } + } + + @Override + public void interrupt() throws UnableToInterruptJobException { + _interrupted = true; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/PlatformParentSystemController.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/PlatformParentSystemController.java new file mode 100644 index 0000000..0f1ba73 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/PlatformParentSystemController.java @@ -0,0 +1,106 @@ +package com.yfd.platform.modules.robotapi.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.component.nettyclient.BootNettyClientChannel; +import com.yfd.platform.component.nettyclient.BootNettyClientChannelCache; +import com.yfd.platform.component.nettyclient.HeartBeatTask; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + *

+ * 前端控制器 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +@RestController +@RequestMapping("/robotapi/platform-parent-system") +@Api(value = "PlatformParentSystemController", tags = "上级平台") +public class PlatformParentSystemController { + + @Resource + private HeartBeatTask heartBeatTask; + @Resource + private IPlatformParentSystemService platformParentSystemService; + + @ApiOperation("获取上级平台信息") + @GetMapping("/getParentSystemPage") + public ResponseResult getParentSystemPage(Page> page, String parentName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(parentName)) { + queryWrapper.like(PlatformParentSystem::getParentName, parentName); + } + Page> mapPage = platformParentSystemService.pageMaps(page, queryWrapper); + return ResponseResult.successData(mapPage); + } + + @Log(module = "上级平台", value = "新增或修改上级参数", type = "1") + @PostMapping("/addOrUpdateParentSystem") + @ApiOperation("新增或修改算法模型") + public ResponseResult addOrUpdateParentSystem(@RequestBody PlatformParentSystem platformParentSystem) { + String isFlag = platformParentSystem.getIsFlag(); + if (StrUtil.isBlank(isFlag)) { + platformParentSystem.setIsFlag("0"); + } + String isEnable = platformParentSystem.getIsEnable(); + if (StrUtil.isBlank(isEnable)) { + platformParentSystem.setIsEnable("0"); + } + if (StrUtil.isBlank(platformParentSystem.getId())) { + platformParentSystem.setCreateDate(DateUtil.now()); + } + platformParentSystemService.saveOrUpdate(platformParentSystem); + return ResponseResult.success(); + } + + @Log(module = "上级平台", value = "删除上级列表", type = "1") + @PostMapping("/deleteParentSystem") + @ApiOperation("删除上级列表") + public ResponseResult deleteParentSystem(String ids) { + List idList = StrUtil.split(ids, ","); + platformParentSystemService.removeByIds(idList); + return ResponseResult.success(); + } + + @PostMapping("/stopSendHeartBeat") + @ApiOperation("停止上级系统心跳发送") + public ResponseResult stopSendHeartBeat(String code) throws InterruptedException { + heartBeatTask.stop("HeartBeatTask"); + BootNettyClientChannel bootNettyClientChannel = BootNettyClientChannelCache.channelMapCache.get(code); + if (bootNettyClientChannel != null) { + bootNettyClientChannel.getChannel().close(); + BootNettyClientChannelCache.channelMapCache.remove(code); + } + // bootNettyClientChannel.getChannel().closeFuture() + platformParentSystemService.update(new LambdaUpdateWrapper().eq(PlatformParentSystem::getChildCode, code).set(PlatformParentSystem::getIsFlag, "0")); + return ResponseResult.success(); + } + + @PostMapping("/sendRegisterToSuper") + @ApiOperation("发送注册信息到上级系统") + public ResponseResult sendRegisterToSuper(String id) { + platformParentSystemService.sendRegisterToSuper(id); + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/RobotOfflineLogController.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/RobotOfflineLogController.java new file mode 100644 index 0000000..2e76af1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/RobotOfflineLogController.java @@ -0,0 +1,22 @@ +package com.yfd.platform.modules.robotapi.controller; + + +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 前端控制器 + *

+ * 离线记录 + * @author zhengsl + * @since 2024-06-26 + */ +@RestController +@RequestMapping("/robotapi/robot-offline-log") +@Api(value = "PlatformParentSystemController", tags = "离线记录") +public class RobotOfflineLogController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/RobotRunLogController.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/RobotRunLogController.java new file mode 100644 index 0000000..e5c88ef --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/RobotRunLogController.java @@ -0,0 +1,22 @@ +package com.yfd.platform.modules.robotapi.controller; + + +import io.swagger.annotations.Api; +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 前端控制器 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +@RestController +@RequestMapping("/robotapi/robot-run-log") +@Api(value = "PlatformParentSystemController", tags = "设备心跳") +public class RobotRunLogController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/StationRobotController.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/StationRobotController.java new file mode 100644 index 0000000..72f77b6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/controller/StationRobotController.java @@ -0,0 +1,254 @@ +package com.yfd.platform.modules.robotapi.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.component.nettyclient.BootNettyClientChannel; +import com.yfd.platform.component.nettyclient.BootNettyClientChannelCache; +import com.yfd.platform.component.nettyclient.HeartBeatTask; +import com.yfd.platform.component.nettyserver.NettyServer; +import com.yfd.platform.component.nettyudpclient.NettyUdpClient; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.FtpClient; +import com.yfd.platform.utils.HttpRESTfulUtils; +import io.netty.channel.Channel; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.awt.*; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +@RestController +@CrossOrigin +@RequestMapping("/stationnode") +@Api(tags = "与边缘节点TCP通信") +public class StationRobotController { + + @Resource + private IStationRobotService staionNodeService; + @Resource + private HeartBeatTask heartBeatTask; + @Resource + private FtpClient ftpClient; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + + double x = 0; + double y = 0; + double z = 10.5; + double a = -1.57; + int type = 0; + + @PostMapping("/sendRobotCommand") + @ApiOperation("发送机器人控制指令") + public ResponseResult sendRobotCommand(String jsonStr) { + staionNodeService.sendRobotCommand(jsonStr); + return ResponseResult.success(); + } + + @PostMapping("/sendRobotModel") + @ApiOperation("发送机器人模型同步") + public ResponseResult sendRobotModel(String robotId) { + staionNodeService.sendRobotModel(robotId); + return ResponseResult.success(); + } + + + @PostMapping("/testCreateCimfile") + @ApiOperation("测试生成cimfile") + public boolean testCreateCimfile() throws Exception { + long eventtime = DateUtil.parse("2023-07-21 12:23:15.321", "yyyy-MM-dd HH:mm:ss.SSS").getTime(); + staionNodeService.createVideocfmresult("0001", "1-" + eventtime, "1"); + return true; + } + + @Log(module = "巡视任务", value = "上报上级系统") + @PostMapping("/reportTaskById") + @ApiOperation("上报上级系统") + public ResponseResult reportParentById(String ids, String type) throws IOException { + staionNodeService.reportParentById(ids, type); + return ResponseResult.success(); + } + + @PostMapping("/testSendLinkComand") + @ApiOperation("发送一键顺控任务") + public ResponseResult testSendLinkComand(String ip, String no, String index, String port) throws Exception { + NettyUdpClient.testSendLinkComand(ip, no, index, port); + return ResponseResult.success(); + } + + @PostMapping("/testSendLinkComand1") + @ApiOperation("发送智能联动任务") + public ResponseResult testSendLinkComand1() throws Exception { + NettyUdpClient.testSendLinkComand1(); + return ResponseResult.success(); + } + + @PostMapping("/markImageRectangle") + @ApiOperation("画框(支持多个)") + public ResponseResult markImageRectangle(String originfilename, String jsonStr, String markedfilename) { + FileUtil.MarkImageRectangle(originfilename, jsonStr, markedfilename); + return ResponseResult.success(); + } + + @PostMapping("/addWaterMark") + @ApiOperation("画水印") + public ResponseResult addWaterMark(String originfilename, String alertinfo, String markedfilename, int pos_x, + int pos_y) { + Color color2 = new Color(226, 7, 24); + Font font = new Font("微软雅黑", Font.PLAIN, 40); + FileUtil.addWaterMark(originfilename, markedfilename, alertinfo, pos_x, pos_y, color2, font); + return ResponseResult.success(); + } + + @PostMapping("/sendRegisterToSuper") + @ApiOperation("发送注册信息到上级系统") + public ResponseResult sendRegisterToSuper() { +// staionNodeService.sendRegisterToSuper(); + return ResponseResult.success(); + } + + @PostMapping("/stopSendHeartBeat") + @ApiOperation("停止上级系统心跳发送") + public ResponseResult stopSendHeartBeat(String code) throws InterruptedException { + heartBeatTask.stop("HeartBeatTask"); + BootNettyClientChannel bootNettyClientChannel = BootNettyClientChannelCache.channelMapCache.get(code); + if(bootNettyClientChannel!=null){ + bootNettyClientChannel.getChannel().close(); + BootNettyClientChannelCache.channelMapCache.remove(code); + } + // bootNettyClientChannel.getChannel().closeFuture() + + return ResponseResult.success(); + } + + @PostMapping("/sendFile") + @ApiOperation("上送文件") + public ResponseResult sendFile(String ip, String port, String username, String password, String filePath, + String roPath) throws KeyManagementException, NoSuchAlgorithmException { + ftpClient.uploadFile(ip, port, username, password, filePath, roPath); + return ResponseResult.success(); + } + + @PostMapping("/monitorVideoSnap") + @ApiOperation("使用ffmpeg截图") + public ResponseResult monitorVideoSnap(String url,String count) { + java.util.List> mapList = new ArrayList<>(); + int size = Integer.parseInt(count); + for (int i = 0; i < size; i++) { + Map map = new HashMap<>(); + map.put("rtsp", url); + map.put("filename", "test" + i+".jpg"); + mapList.add(map); + } + ExecutorService executorService = Executors.newFixedThreadPool(size); + for (Map map : mapList) { + executorService.submit(() -> { + // httpRESTfulUtils.getMonitorVideoSnap("E:\\ffmpeg\\bin\\", map.get("rtsp"), map.get + // ("filename")); + }); + } + return ResponseResult.success(); + } + + @PostMapping("/sendCoordinatePixel") + @ApiOperation("回到原点") + public ResponseResult sendCoordinatePixel() { + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + x = 0; + y = 10.5; + z = 0; + a = -1.57; + type = 0; + jsonObject.putOpt("patroldevice_code", "34020000002010000001"); + jsonObject.putOpt("patroldevice_name", "43020000001180003001"); + jsonObject.putOpt("coordinate_geography", "115.70374298095703,34.30839920043945"); + jsonObject.putOpt("time", "2024-11-08 09:11:13"); + String coordinate_pixel = x + "," + y + "," + z + "," + a; + jsonObject.putOpt("coordinate_pixel", coordinate_pixel); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + new Thread(() -> { + try { + while (type >= 0) { + Thread.sleep(1000); + if (type == 0) { + continue; + } + if (type == 5) { + break; + } + switch (type) { + case 1: + y += 1; + break; + case 2: + y -= 1; + break; + case 3: + z -= 1; + a=0; + break; + case 4: + a=3.14; + z += 1; + break; + case 6: + x += 0.15; + break; + case 8: + x += 1; + z += 1; + break; + default: + z += 0.15; + break; + } + + String pixel = x + "," + y + "," + z + "," + a; + JSONArray jsonArray1 = new JSONArray(); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.putOpt("patroldevice_code", "34020000002010000001"); + jsonObject1.putOpt("patroldevice_name", "43020000001180003001"); + jsonObject1.putOpt("coordinate_geography", "115.70374298095703,34.30839920043945"); + jsonObject1.putOpt("time", "2024-11-08 09:11:13"); + jsonObject1.putOpt("coordinate_pixel", pixel); + jsonArray1.add(jsonObject1); + WebSocketServer.sendInfo("robot_status_data", jsonArray1.toString()); + } + } catch (Exception e) { + log.error(e.getMessage());; + } + }).start(); + return ResponseResult.success(); + } + + @PostMapping("/setType") + @ApiOperation("设置方向") + public ResponseResult setType(String typeStr) { + type = NumberUtil.parseInt(typeStr); + return ResponseResult.success(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/PlatformParentSystem.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/PlatformParentSystem.java new file mode 100644 index 0000000..1d01ab9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/PlatformParentSystem.java @@ -0,0 +1,113 @@ +package com.yfd.platform.modules.robotapi.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_platform_parent_system") +public class PlatformParentSystem implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * guid + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + + /** + * 变电站编码 + */ + private String stationCode; + + /** + * 主站编码 + */ + private String parentCode; + + /** + * 主站名称 + */ + private String parentName; + + /** + * 子站编码 + */ + private String childCode; + + /** + * 主站ip + */ + private String serverIp; + + /** + * 主站端口 + */ + private String serverPort; + + /** + * 协议版本号 + */ + private String serverVersion; + + /** + * ftpip地址 + */ + private String ftpIp; + + /** + * ftp端口号 + */ + private String ftpPort; + + /** + * ftp用户名 + */ + private String ftpUser; + + /** + * ftp密码 + */ + private String ftpPassword; + + /** + * 默认0;0:显示;1:隐式 + */ + private String ftpType; + + /** + * 链接状态,默认0;0:未连接;1:已连接 + */ + private String isFlag; + + /** + * 是否启用;0:禁用;1:启用 + */ + private String isEnable; + + /** + * 备注 + */ + private String comment; + + /** + * 创建时间 + */ + private String createDate; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/RobotOfflineLog.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/RobotOfflineLog.java new file mode 100644 index 0000000..3e53aec --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/RobotOfflineLog.java @@ -0,0 +1,58 @@ +package com.yfd.platform.modules.robotapi.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author zhengsl + * @since 2024-06-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_robot_offline_log") +public class RobotOfflineLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * guid + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 离线开始时间 + */ + private LocalDateTime offlineStartTime; + + /** + * 巡视设备唯一标识 + */ + private String robotCode; + + /** + * 巡视系统唯一标识 + */ + private String patrolServerCode; + + /** + * 累计离线次数,自增 + */ + private String offlineSum; + + /** + * 离线结束时间 + */ + private LocalDateTime offlineEndTime; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/RobotRunLog.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/RobotRunLog.java new file mode 100644 index 0000000..4685a2a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/RobotRunLog.java @@ -0,0 +1,64 @@ +package com.yfd.platform.modules.robotapi.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import java.time.LocalDateTime; +import java.io.Serializable; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_robot_run_log") +public class RobotRunLog implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 报文类型 + */ + private String type; + + /** + * 报文指令 + */ + private String command; + + /** + * 编码 + */ + private String code; + + /** + * 报文内容 + */ + private String xmlContent; + + /** + * 上报时间 + */ + private LocalDateTime reportTime; + + /** + * 巡视设备唯一标识 + */ + private String robotCode; + + /** + * 巡视系统唯一标识 + */ + private String patrolServerCode; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/StationnodeTranslog.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/StationnodeTranslog.java new file mode 100644 index 0000000..262c764 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/domain/StationnodeTranslog.java @@ -0,0 +1,71 @@ +package com.yfd.platform.modules.robotapi.domain; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 巡视主机与变电站边缘节点数据同步日志 + *

+ * + * + * @since 2023-06-29 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("iis_stationnode_translog") +public class StationnodeTranslog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + private String id; + + /** + * 变电站编号 + */ + private String stationCode; + + /** + * 变电站名称 + */ + private String stationName; + + /** + * 传输时间 + */ + private Timestamp transTime; + + /** + * 传输信息类型 + */ + private String transtype; + + /** + * 传输信息描述 + */ + private String transdesc; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/PlatformParentSystemMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/PlatformParentSystemMapper.java new file mode 100644 index 0000000..5495a5a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/PlatformParentSystemMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.robotapi.mapper; + +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +public interface PlatformParentSystemMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/RobotOfflineLogMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/RobotOfflineLogMapper.java new file mode 100644 index 0000000..f24c571 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/RobotOfflineLogMapper.java @@ -0,0 +1,29 @@ +package com.yfd.platform.modules.robotapi.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.robotapi.domain.RobotOfflineLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import java.util.List; +import java.util.Map; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zhengsl + * @since 2024-06-26 + */ +public interface RobotOfflineLogMapper extends BaseMapper { + + List> getOnlineDurationList(String robotCode, String patrolServerCode); + + List> getOfflineTimes(String robotCode); + + Page> getOnlineDurationPage(Page> page, String stationId, String patrolDeviceCode, String patrolDeviceName, String type); + + List> normalInspectionDays(String robotCode); + + List> normalRunDays(String robotCode); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/RobotRunLogMapper.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/RobotRunLogMapper.java new file mode 100644 index 0000000..e1c69c5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/mapper/RobotRunLogMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.robotapi.mapper; + +import com.yfd.platform.modules.robotapi.domain.RobotRunLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +public interface RobotRunLogMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IPlatformParentSystemService.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IPlatformParentSystemService.java new file mode 100644 index 0000000..8cc31e2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IPlatformParentSystemService.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.robotapi.service; + +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.io.IOException; + +/** + *

+ * 服务类 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +public interface IPlatformParentSystemService extends IService { + + void sendTaskReportCommand(String param) throws Exception; + void sendRegisterToSuper(String id); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IRobotOfflineLogService.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IRobotOfflineLogService.java new file mode 100644 index 0000000..d527f1b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IRobotOfflineLogService.java @@ -0,0 +1,28 @@ +package com.yfd.platform.modules.robotapi.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.robotapi.domain.RobotOfflineLog; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务类 + *

+ * + * @author zhengsl + * @since 2024-06-26 + */ +public interface IRobotOfflineLogService extends IService { + + List> getOnlineDurationList(String robotCoe, String patrolServerCode); + Page> getOnlineDurationPage(Page> page,String stationId,String patrolDeviceCode,String patrolDeviceName,String type); + + List> getOfflineTimes(String robotCode); + + List> normalInspectionDays(String robotCode); + + List> normalRunDays(String robotCode); +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IRobotRunLogService.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IRobotRunLogService.java new file mode 100644 index 0000000..2278b6b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IRobotRunLogService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.modules.robotapi.service; + +import com.yfd.platform.modules.robotapi.domain.RobotRunLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 服务类 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +public interface IRobotRunLogService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IStationRobotService.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IStationRobotService.java new file mode 100644 index 0000000..c800a31 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/IStationRobotService.java @@ -0,0 +1,116 @@ +package com.yfd.platform.modules.robotapi.service; + +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.modules.basedata.domain.Substation; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +/** + *

+ * 巡视任务表 服务类 + *

+ * + * + * @since 2023-04-08 + */ +public interface IStationRobotService extends IService { + /******************--------以下为巡视主机下发消息服务-----***********************************/ + + + + /********************************** + * 用途说明: 更新机器人在线状态 + * 参数说明 stationNodeIP 变电站边缘节点IP, + * online:1=在线,0-离线 + * 返回值说明: boolean + ***********************************/ + boolean updateRobotOnline(String robotIp,String online); + + + /********************************** + * 用途说明: 通知变电站同步更新基础数据 + * 参数说明 stationcode 变电站编号 + * 返回值说明: boolean + ***********************************/ + boolean sendMsgOfSyncBaseData(String stationcode); + + + void sendCommand(String targetIp,String sendcode,String receivecode,String type,String command,String code,String items); + + /********************************** + * 用途说明: 巡视主机向边缘节点下发巡视任务 + * 参数说明 stationcode 变电站编号,String taskid + * 返回值说明: boolean + ***********************************/ + void sendMsgOfTask(String stationcode,String taskid); + + /********************************** + * 用途说明: 巡视主机向边缘节点下发巡视任务控制指令 + * 参数说明 stationcode 变电站编号,todotaskid 巡视任务id, + * String command 0-巡视任务终止 1-巡视任务启动(生效) 2-任务执行暂停 3-任务执行恢复 4-任务执行停止 5-任务手动执行 + * 返回值说明: boolean + ***********************************/ + void sendMsgOfTaskCommand(String stationcode,String todotaskid,String command); + + /********************************** + * 用途说明: 巡视主机向边缘节点下发检修计划 + * 参数说明 stationcode 变电站编号,String planid + * 返回值说明: boolean + ***********************************/ + void sendMsgOfExaminePlan(String stationcode,String planid); + + /********************************** + * 用途说明: 巡视主机向边缘节点下发联动信息 + * 参数说明 stationcode 变电站编号,String signalid + * 返回值说明: boolean + ***********************************/ + void sendMsgOfLinkageSignal(String stationcode,String signalid); + + /******************--------以下为业务数据交互处理服务-----***********************************/ + + + /********************************** + * 用途说明: 接收任务信息并同步 + * 参数说明 jsonStr + * 返回值说明: cn.hutool.json.JSONObject + ***********************************/ + JSONObject getTaskInfo(String jsonStr); + + + + /********************************** + * 用途说明: 生成一键顺控反馈结果文件 + * 参数说明 stationcode 变电站编号 taskcode 任务编号 result 视频反馈结果 + * 返回值说明: 创建结果确认文件并下发到边缘节点 + ***********************************/ + void createVideocfmresult(String stationcode,String taskcode,String result) throws UnsupportedEncodingException; + + + /********************************** + * 用途说明: 向边缘节点发送机器人控制指令 + * 参数说明 jsonStr + * 返回值说明: void + ***********************************/ + void sendRobotCommand(String jsonStr); + + /********************************** + * 用途说明: 向上级发送数据 + * 参数说明 jsonStr + * 返回值说明: void + ***********************************/ + void sendParentData(String SendCode, String ReceiveCode, String Type, String Command, String Code, + String Items) throws IOException; + + /********************************** + * 用途说明: 上报上级系统 + * 参数说明 ids + * 参数说明 type + * 返回值说明: boolean + ***********************************/ + String reportParentById(String ids, String type) throws IOException; + + void sendRobotModel(String robotId); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/PlatformParentSystemServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/PlatformParentSystemServiceImpl.java new file mode 100644 index 0000000..e718702 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/PlatformParentSystemServiceImpl.java @@ -0,0 +1,138 @@ +package com.yfd.platform.modules.robotapi.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.component.nettyclient.*; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.mapper.SubstationMapper; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.patroltask.domain.AlarmLog; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.mapper.AlarmLogMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.domain.PlatformParentSystem; +import com.yfd.platform.modules.robotapi.mapper.PlatformParentSystemMapper; +import com.yfd.platform.modules.robotapi.service.IPlatformParentSystemService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.robotapi.service.IRobotOfflineLogService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.FtpClient; +import com.yfd.platform.utils.TLSFTPUtils; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.CharsetUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.net.URLDecoder; +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务实现类 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +@Service +public class PlatformParentSystemServiceImpl extends ServiceImpl implements IPlatformParentSystemService { + + private static final Logger logger = LoggerFactory.getLogger(PlatformParentSystemServiceImpl.class); + @Resource + private IStationRobotService stationRobotService; + + @Resource + private ITaskTodoService taskTodoService; + @Resource + private FtpClient ftpClient; + @Resource + private HttpServerConfig httpServerConfig; + @Resource + private IRobotOfflineLogService robotOfflineLogService; + @Resource + private TLSFTPUtils tlsftpUtils; + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + @Resource + private AlarmLogMapper alarmLogMapper; + + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private SubstationMapper substationMapper; + + + + @Override + public void sendTaskReportCommand(String param) throws Exception { + List list = this.list(); + if (list.size() <= 0) { + return; + } + PlatformParentSystem parentSystem = list.get(0); + this.sendData(parentSystem.getChildCode(), parentSystem.getParentCode(), "69", "", parentSystem.getStationCode(), param); + } + + @Override + public void sendRegisterToSuper(String id) { + PlatformParentSystem platformParentSystem = this.getById(id); + if (platformParentSystem == null) { + return; + } + ParentConfig parentConfig = new ParentConfig(); + parentConfig.setParentId(platformParentSystem.getParentCode()); + parentConfig.setServerId(platformParentSystem.getChildCode()); + parentConfig.setTcpPort(platformParentSystem.getServerPort()); + parentConfig.setParentIp(platformParentSystem.getServerIp()); + BootNettyClientThread thread = new BootNettyClientThread(platformParentSystem.getServerIp(), + Integer.parseInt(platformParentSystem.getServerPort()), parentConfig); + thread.start(); + } + + /********************************** + * 用途说明: 主动发送数据 + * 参数说明 ctx + * 参数说明 msg + * 参数说明 reponsexml + * 返回值说明: void + ***********************************/ + public void sendData(String serverCode, String parentCode, String Type, String Command, String Code, String Items) { + + String xml = MyXmlUtil.getXml(serverCode, parentCode, Type, Command, Code, Items); + logger.info("主动发送数据" + xml); + WebSocketServer.sendInfo("robotinfo", xml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getRequestSessionID()); + messageProtocol.setReceiverSerialNo(0L); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if (BootNettyClientChannelCache.channelMapCache.size() > 0) { + BootNettyClientChannel bootNettyChannel = BootNettyClientChannelCache.channelMapCache.get(serverCode); + if (bootNettyChannel != null && bootNettyChannel.getChannel().isOpen()) { + bootNettyChannel.getChannel().writeAndFlush(messageProtocol); + } + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/RobotOfflineLogServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/RobotOfflineLogServiceImpl.java new file mode 100644 index 0000000..738c562 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/RobotOfflineLogServiceImpl.java @@ -0,0 +1,52 @@ +package com.yfd.platform.modules.robotapi.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.modules.robotapi.domain.RobotOfflineLog; +import com.yfd.platform.modules.robotapi.mapper.RobotOfflineLogMapper; +import com.yfd.platform.modules.robotapi.service.IRobotOfflineLogService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + *

+ * 服务实现类 + *

+ * + * @author zhengsl + * @since 2024-06-26 + */ +@Service +public class RobotOfflineLogServiceImpl extends ServiceImpl implements IRobotOfflineLogService { + + @Resource + private RobotOfflineLogMapper robotOfflineLogMapper; + + @Override + public List> getOnlineDurationList(String robotCode, String patrolServerCode) { + return robotOfflineLogMapper.getOnlineDurationList(robotCode, patrolServerCode); + } + + @Override + public Page> getOnlineDurationPage(Page> page,String stationId,String patrolDeviceCode,String patrolDeviceName,String type) { + return robotOfflineLogMapper.getOnlineDurationPage(page,stationId,patrolDeviceCode,patrolDeviceName,type); + } + + @Override + public List> getOfflineTimes(String robotCode) { + return robotOfflineLogMapper.getOfflineTimes(robotCode); + } + + @Override + public List> normalInspectionDays(String robotCode) { + return robotOfflineLogMapper.normalInspectionDays(robotCode); + } + + @Override + public List> normalRunDays(String robotCode) { + return robotOfflineLogMapper.normalRunDays(robotCode); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/RobotRunLogServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/RobotRunLogServiceImpl.java new file mode 100644 index 0000000..10c1000 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/RobotRunLogServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.modules.robotapi.service.impl; + +import com.yfd.platform.modules.robotapi.domain.RobotRunLog; +import com.yfd.platform.modules.robotapi.mapper.RobotRunLogMapper; +import com.yfd.platform.modules.robotapi.service.IRobotRunLogService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author zhengsl + * @since 2024-06-24 + */ +@Service +public class RobotRunLogServiceImpl extends ServiceImpl implements IRobotRunLogService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/StationRobotServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/StationRobotServiceImpl.java new file mode 100644 index 0000000..b64a354 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotapi/service/impl/StationRobotServiceImpl.java @@ -0,0 +1,663 @@ +package com.yfd.platform.modules.robotapi.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.component.nettyclient.BootNettyClientChannel; +import com.yfd.platform.component.nettyclient.BootNettyClientChannelCache; +import com.yfd.platform.component.nettyclient.BootNettyClientThread; +import com.yfd.platform.component.nettyserver.BootSessionCache; +import com.yfd.platform.component.nettyserver.ChannelMap; +import com.yfd.platform.component.nettyserver.MyMessageProtocol; +import com.yfd.platform.component.nettyserver.MyXmlUtil; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ParentConfig; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.constant.SystemCode; +import com.yfd.platform.modules.basedata.domain.*; +import com.yfd.platform.modules.basedata.mapper.*; +import com.yfd.platform.modules.patroltask.domain.ExaminePlan; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.ExaminePlanMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import com.yfd.platform.utils.FileUtil; +import io.netty.channel.Channel; +import io.netty.util.CharsetUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.*; + +/** + *

+ * 巡视任务表 服务实现类 + *

+ * + * @since 2023-04-08 + */ +@Service +@Slf4j +public class StationRobotServiceImpl extends ServiceImpl implements IStationRobotService { + + @Resource + private SubstationMapper substationMapper; + + @Resource + private HttpServerConfig serverConfig; + @Resource + private SubstationDeviceMapper substationDeviceMapper; + @Resource + private SubstationPatroldeviceMapper substationPatroldeviceMapper; + @Resource + private TaskMapper taskMapper; + @Resource + private ITaskTodoService taskTodoService; + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private ExaminePlanMapper examinePlanMapper; + @Resource + private LinkageSignalMapper linkageSignalMapper; + @Resource + private ParentConfig parentConfig; + + @Resource + private RedisTemplate redisTemplate; + + @Value("${file-space.confirmfilepath}") + private String confirmfilepath; + + @Value("${file-space.system}") + private String systempath; + + + @Override + public boolean updateRobotOnline(String robotIp, String online) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SubstationPatroldevice::getIpaddr, robotIp).set(SubstationPatroldevice::getOnline, online); + substationPatroldeviceMapper.update(null, updateWrapper); + return true; + } + + @Override + public boolean sendMsgOfSyncBaseData(String stationcode) { + //根据变电站编号获取变电站对象信息 + List list = substationMapper.selectByStaionCode(stationcode); + if (list.size() == 0) { + return false; + } + Substation substation = list.get(0); + + //向机器人发送基础数据同步指令 + sendMsgtoRobot(substation.getStationIp(), serverConfig.getPatrolServerid(), substation.getNodeId(), "250", + "1", stationcode, null); + + return true; + } + + @Override + public void sendCommand(String targetIp, String sendcode, String receivecode, String type, String command, + String code, String items) { + log.info("进入sendCommand方法"); + if (StrUtil.isBlank(targetIp)) { + log.info("判断机器人"); + // 机器人编码 + List list = + substationPatroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getPatroldeviceCode, code)); + // 发送机器人控制指令 + if (list.size() > 0) { + SubstationPatroldevice substationPatroldevice = list.get(0); + sendMsgtoRobot(substationPatroldevice.getIpaddr(), sendcode, substationPatroldevice.getCustom3(), type, command, code, items); + } + } else { + sendMsgtoRobot(targetIp, sendcode, receivecode, type, command, code, items); + } + + + } + + @Override + public void sendMsgOfTask(String stationcode, String taskid) { + //根据变电站编号获取变电站对象信息 + List list = substationMapper.selectByStaionCode(stationcode); + if (list.size() == 0) { + return; + } + Substation substation = list.get(0); + + //根据巡视任务ID获取巡视任务信息 + Task task = taskMapper.selectById(taskid); + String jsonstr = JSONUtil.toJsonStr(task); + + //向机器人发送巡视任务数据 + sendMsgtoRobot(substation.getStationIp(), serverConfig.getPatrolServerid(), substation.getNodeId(), SystemCode.TYPE_TASK_ISSUED_CODE.getCode(), "1" + , stationcode, jsonstr); + + } + + @Override + public void sendMsgOfTaskCommand(String stationcode, String todotaskid, String command) { + //根据变电站编号获取变电站对象信息 + List list = substationMapper.selectByStaionCode(stationcode); + if (list.size() == 0) { + return; + } + Substation substation = list.get(0); + //向机器人发送巡视任务数据 + sendMsgtoRobot(substation.getStationIp(), serverConfig.getPatrolServerid(), substation.getNodeId(), SystemCode.TYPE_TASK_CONTROL_CODE.getCode(), + command, todotaskid, null); + + } + + @Override + public void sendMsgOfExaminePlan(String stationcode, String planid) { + //根据变电站编号获取变电站对象信息 + List list = substationMapper.selectByStaionCode(stationcode); + if (list.size() == 0) { + return; + } + Substation substation = list.get(0); + + //根据检修计划 ID获取巡视任务信息 + ExaminePlan plan = examinePlanMapper.selectById(planid); + String jsonstr = JSONUtil.toJsonStr(plan); + + //向变电站发送检修计划数据 + sendMsgtoRobot(substation.getStationIp(), serverConfig.getPatrolServerid(), substation.getNodeId(), SystemCode.TYPE_EXAMINE_ISSUED_CODE.getCode(), SystemCode.COMMAND_EXAMINE_CONFIG_CODE.getCode() + , stationcode, jsonstr); + } + + @Override + public void sendMsgOfLinkageSignal(String stationcode, String signalid) { + //根据变电站编号获取变电站对象信息 + List list = substationMapper.selectByStaionCode(stationcode); + if (list.size() == 0) { + return; + } + Substation substation = list.get(0); + + //根据信号ID获取联动信号 + LinkageSignal signal = linkageSignalMapper.selectById(signalid); + String jsonstr = JSONUtil.toJsonStr(signal); + + //向机器人发送联动信号配置 + sendMsgtoRobot(substation.getStationIp(), serverConfig.getPatrolServerid(), substation.getNodeId(), "71", "1" + , stationcode, jsonstr); + + } + + + /** + * 机器人发送消息 + * + * @param targetip 变电站IP + * sendcode 发送方GBId + * receivecode 接收方BGId + * type 巡视系统规范中定义 + * command 巡视系统中规范定 + * code 发送的编码 + * items 发送的内容数据信息封装成jsonstr + */ + private void sendMsgtoRobot(String targetip, String sendcode, String receivecode, String type, String command, + String code, String items) { + log.info("接收控制值targetip" + targetip + "接收控制值sendcode" + sendcode + "接收控制值receivecode" + receivecode + "接收控制值items" + items); + String reponsexml = MyXmlUtil.getXml(sendcode, receivecode, type, command, code, items); + log.info("主动发送控制命令" + reponsexml); + MyMessageProtocol messageProtocol = new MyMessageProtocol(); + long requestSessionID = BootSessionCache.getRequestSessionID(); + redisTemplate.opsForValue().set(Constant.ROBOT_SEND_CODE + requestSessionID, reponsexml); + messageProtocol.setSenderSerialNo(requestSessionID); + //接收者回复的会话ID,发送时赋0 + messageProtocol.setReceiverSerialNo(0L); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(reponsexml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(reponsexml.getBytes(CharsetUtil.UTF_8)); + Channel channel = ChannelMap.getChannel(targetip); + if (null == channel) { + throw new RuntimeException("客户端已离线"); + } + channel.writeAndFlush(messageProtocol); + } + + /********************************** + * 用途说明: 接收任务信息并同步 + * 参数说明 jsonStr + * 返回值说明: cn.hutool.json.JSONObject + ***********************************/ + @Transactional(rollbackFor = Exception.class) + @Override + public JSONObject getTaskInfo(String jsonStr) { + JSONObject jsonObject = JSONUtil.parseObj(jsonStr); + // 向数据库中插入或更新任务 + insertTask(jsonObject); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.putOpt("code", 200); + return jsonObject1; + } + + /********************************** + * 用途说明: 向数据库中插入或更新任务 + * 参数说明 jsonObject + * 返回值说明: void + ***********************************/ + private void insertTask(JSONObject jsonObject) { + for (String key : jsonObject.keySet()) { + JSONArray jsonArray = jsonObject.getJSONArray(key); + if ("task".equals(key)) { + List taskList = JSONUtil.toList(jsonArray, Task.class); + taskMapper.batchAdd(taskList); + } else if ("task_todo".equals(key)) { + List taskList = JSONUtil.toList(jsonArray, TaskTodo.class); + taskTodoService.saveOrUpdateBatch(taskList); + } else if ("task_result".equals(key)) { + List taskList = JSONUtil.toList(jsonArray, TaskResult.class); + taskResultMapper.batchAdd(taskList); + } + } + } + + @Override + public void createVideocfmresult(String stationcode, String taskcode, String videoresult) throws UnsupportedEncodingException { + String[] temp = taskcode.split("-"); + //监控索引号 + String monitorindex = temp[0]; + //事件时标 + String eventtime = DateUtil.format(DateUtil.date(Long.parseLong(temp[1])), "yyyy-MM-dd HH:mm:ss.SSS"); + + List> tasklist = + taskMapper.selectMaps(new LambdaQueryWrapper().eq(Task::getTaskCode, taskcode)); +// String value1 = "";//一键顺控的原始值 + String equip = ""; + List linkageSignals = + linkageSignalMapper.selectList(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, monitorindex)); + String stationNum = ""; + if (tasklist.size() > 0) {//任务表 +// value1 = tasklist.get(0).get("custom3").toString(); + if (linkageSignals.size() > 0) { + LinkageSignal linkageSignal = linkageSignals.get(0); + stationNum = linkageSignal.getStationNum(); + String linkageDeviceName = linkageSignal.getLinkageDeviceName(); + equip = StrUtil.subBefore(linkageDeviceName, "-", true); + } + } +// String result = "分析识别"; +// if (value1.equals("合位") && videoresult.equals("刀闸关闭")) { +// result = "合闸正常"; +// } +// if (value1.equals("合位") && (videoresult.equals("中间状态") || videoresult.equals("刀闸打开"))) { +// result = "合闸异常"; +// } +// if (value1.equals("分位") && videoresult.equals("刀闸打开")) { +// result = "分闸正常"; +// } +// if (value1.equals("分位") && (videoresult.equals("中间状态") || videoresult.equals("刀闸关闭"))) { +// result = "分闸异常"; +// } + String cimfilecontent = getReturnCimFile(stationNum, monitorindex, equip, videoresult, eventtime); + String filename = confirmfilepath + "videocfmresult.cime"; + FileUtil.writeString(cimfilecontent, filename, Charset.forName("GB2312")); + + } + + + /********************************** + * 用途说明: 向边缘节点发送机器人控制指令 + * 参数说明 jsonStr + * 返回值说明: void + ***********************************/ + @Override + public void sendRobotCommand(String jsonStr) { + JSONObject jsonObject = JSONUtil.parseObj(jsonStr); + this.sendCommand("", serverConfig.getPatrolServerid(), serverConfig.getRobotId(), jsonObject.getStr("Type"), jsonObject.getStr("Command"), jsonObject.getStr("Code"), jsonObject.getStr("Items")); + } + + /********************************** + * 用途说明: 向上级发送设备状态 + * 参数说明 jsonStr + * 返回值说明: void + ***********************************/ + @Override + public void sendParentData(String SendCode, String ReceiveCode, String Type, String Command, String Code, + String Items) throws IOException { + + log.info("向上级系统发送信息SendCode"+SendCode); + if (StrUtil.isBlank(SendCode)) { + return; + } + String xml = com.yfd.platform.component.nettyclient.MyXmlUtil.getXml(SendCode, ReceiveCode, Type, Command, + Code, Items); + WebSocketServer.sendInfo("robotinfo", xml); + log.info("向上级系统发送信息" + xml); + com.yfd.platform.component.nettyclient.MyMessageProtocol messageProtocol = + new com.yfd.platform.component.nettyclient.MyMessageProtocol(); + messageProtocol.setSenderSerialNo(BootSessionCache.getRequestSessionID()); + messageProtocol.setReceiverSerialNo(new Long(0L)); + messageProtocol.setSessionflag(0); + messageProtocol.setXmllen(xml.getBytes(CharsetUtil.UTF_8).length); + messageProtocol.setXmlcontent(xml.getBytes(CharsetUtil.UTF_8)); + if (BootNettyClientChannelCache.channelMapCache.size() > 0) { + for (Map.Entry entry : + BootNettyClientChannelCache.channelMapCache.entrySet()) { + BootNettyClientChannel bootNettyChannel = entry.getValue(); + if (bootNettyChannel != null && bootNettyChannel.getChannel().isOpen()) { + bootNettyChannel.getChannel().writeAndFlush(messageProtocol); + } else { +// BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), +// Integer.parseInt(parentConfig.getTcpPort()), parentConfig); +// thread.start(); + log.error("============================存在通道但是已经关闭============================="+BootNettyClientChannelCache.channelMapCache.size()); + } + } + } else { + log.error("============================没有找到合适的通道============================="+BootNettyClientChannelCache.channelMapCache.size()); +// BootNettyClientThread thread = new BootNettyClientThread(parentConfig.getParentIp(), +// Integer.parseInt(parentConfig.getTcpPort()), parentConfig); +// thread.start(); + } + } + + /********************************** + * 用途说明: 上报上级系统 + * 参数说明 ids + * 参数说明 type + * 返回值说明: boolean + ***********************************/ + @Override + public String reportParentById(String ids, String type) throws IOException { + String filePath = ""; + List> mapList = new ArrayList<>(); + String xmlPath = ""; + String stationCode="7FE4BB37-F54A-4808-852B-6CAB43DD71B3-00001"; + switch (type) { + case "1": + List substations = + substationMapper.selectList(new LambdaQueryWrapper().eq(Substation::getDatastatus + , "1")); + for (Substation substation : substations) { + Map map = new HashMap<>(); + map.put("station_code", substation.getStationCode()); + map.put("station_name", substation.getStationName()); + map.put("station_ip", substation.getStationIp()); + map.put("section_id", substation.getSectionId()); + map.put("section_name", substation.getSectionName()); + map.put("station_address", substation.getStationAddress()); + map.put("volt_level", substation.getVoltLevel()); + map.put("city_name", substation.getCityName()); + map.put("province_name", substation.getProvinceName()); + map.put("company_name", substation.getCompanyName()); + map.put("contact_phone", substation.getContactPhone()); + map.put("contact_person", substation.getContactPerson()); + map.put("introduction", substation.getIntroduction()); + mapList.add(map); + } + filePath = String.format("%s_%s_%s.xml", "host", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Host_Model"); + break; + case "2": + List robots = + substationPatroldeviceMapper.selectList(new LambdaQueryWrapper().in(SubstationPatroldevice::getType, "1", "2", "3")); + for (SubstationPatroldevice patroldevice : robots) { + Map map = new HashMap<>(); + map.put("patroldevice_code", patroldevice.getPatroldeviceCode()); + map.put("patroldevice_name", patroldevice.getPatroldeviceName()); + map.put("station_name", patroldevice.getStationName()); + map.put("station_code", patroldevice.getStationCode()); + map.put("device_model", patroldevice.getAreaId()); + map.put("manufacturer", patroldevice.getAreaName()); + map.put("use_unit", patroldevice.getUseUnit()); + map.put("device_source", patroldevice.getDeviceSource()); + map.put("production_date", patroldevice.getProductionDate()); + map.put("production_code", patroldevice.getProductionDate()); + map.put("istransport", patroldevice.getIstransport()); + map.put("use_mode", patroldevice.getUseMode()); + map.put("video_mode", patroldevice.getVideoMode()); + map.put("place", patroldevice.getPlace()); + map.put("type", patroldevice.getType()); + map.put("patroldevice_info", patroldevice.getPatroldeviceInfo()); + map.put("robots_code", patroldevice.getRobotsCode()); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "robot_model_ZzServer01"); +// filePath=String.format("%s_%s_%s.xml", "robot", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Robot_Model"); + break; + case "3": + List patroldevices = + substationPatroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getType, "10")); + for (SubstationPatroldevice patroldevice : patroldevices) { + Map map = new HashMap<>(); + map.put("patroldevice_code", patroldevice.getPatroldeviceCode()); + map.put("patroldevice_name", patroldevice.getPatroldeviceName()); + map.put("station_name", patroldevice.getStationName()); + map.put("station_code", patroldevice.getStationCode()); + map.put("device_model", patroldevice.getAreaId()); + map.put("manufacturer", patroldevice.getAreaName()); + map.put("use_unit", patroldevice.getUseUnit()); + map.put("device_source", patroldevice.getDeviceSource()); + map.put("production_date", patroldevice.getProductionDate()); + map.put("production_code", patroldevice.getProductionDate()); + map.put("istransport", patroldevice.getIstransport()); + map.put("use_mode", patroldevice.getUseMode()); + map.put("video_mode", patroldevice.getVideoMode()); + map.put("place", patroldevice.getPlace()); + map.put("type", patroldevice.getType()); + map.put("patroldevice_info", patroldevice.getPatroldeviceInfo()); + map.put("robots_code", patroldevice.getRobotsCode()); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "camera_model_ZzServer01"); +// filePath =String.format("%s_%s_%s.xml", "camera", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "PatrolDevice_Model"); + break; + case "4": + List substationDevices = substationDeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationDevice::getDataType,"0x01").ne(SubstationDevice::getLastmodifier,"机器人系统")); + for (SubstationDevice substationDevice : substationDevices) { + Map map = new HashMap<>(); + map.put("station_name", substationDevice.getStationName()); + map.put("station_code", substationDevice.getStationCode()); + map.put("area_id", substationDevice.getAreaId()); + map.put("area_name", substationDevice.getAreaName()); + map.put("device_id", substationDevice.getDeviceId()); + map.put("device_name", substationDevice.getDeviceName()); + map.put("component_id", substationDevice.getComponentId()); + map.put("component_name", substationDevice.getComponentName()); + map.put("bay_id", substationDevice.getBayId()); + map.put("bay_name", substationDevice.getBayName()); + map.put("main_device_id", substationDevice.getMainDeviceId()); + map.put("main_device_name", substationDevice.getMainDeviceName()); + map.put("device_type", substationDevice.getDeviceType()); + map.put("point_type", substationDevice.getDeviceClass()); + map.put("meter_type", substationDevice.getMeterType()); + map.put("appearance_type", substationDevice.getAppearanceType()); + map.put("save_type_list", substationDevice.getSaveTypeList()); + map.put("recognition_type_list", substationDevice.getRecognitionTypeList()); + map.put("phase", substationDevice.getPhase()); + map.put("device_info", substationDevice.getDeviceInfo()); + String dataType = substationDevice.getDataType(); + int datatype=1; + if("0x03".equals(dataType)){ + datatype=3; + } + if("0x02".equals(dataType)){ + datatype=2; + } + map.put("data_type", datatype); + map.put("lower_value", substationDevice.getLowerValue()); + map.put("upper_value", substationDevice.getUpperValue()); + String videoPos = substationDevice.getVideoPos(); + map.put("video_pos", videoPos); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "device_model_ZzServer01"); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Device_Model"); + break; + case "5": + List uavList = + substationPatroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getType, "13")); + for (SubstationPatroldevice patroldevice : uavList) { + Map map = new HashMap<>(); + map.put("patroldevice_code", patroldevice.getPatroldeviceCode()); + map.put("patroldevice_name", patroldevice.getPatroldeviceName()); + map.put("station_name", patroldevice.getStationName()); + map.put("station_code", patroldevice.getStationCode()); + map.put("device_model", patroldevice.getAreaId()); + map.put("manufacturer", patroldevice.getAreaName()); + map.put("use_unit", patroldevice.getUseUnit()); + map.put("device_source", patroldevice.getDeviceSource()); + map.put("production_date", patroldevice.getProductionDate()); + map.put("production_code", patroldevice.getProductionDate()); + map.put("istransport", patroldevice.getIstransport()); + map.put("use_mode", patroldevice.getUseMode()); + map.put("video_mode", patroldevice.getVideoMode()); + map.put("place", patroldevice.getPlace()); + map.put("type", patroldevice.getType()); + map.put("patroldevice_info", patroldevice.getPatroldeviceInfo()); + map.put("robots_code", patroldevice.getRobotsCode()); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "uav_model_ZzServer01"); +// filePath=String.format("%s_%s_%s.xml", "uav", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Uav_Model"); + break; + case "6": + List vocalList = + substationPatroldeviceMapper.selectList(new LambdaQueryWrapper().eq(SubstationPatroldevice::getType, "14")); + for (SubstationPatroldevice patroldevice : vocalList) { + Map map = new HashMap<>(); + map.put("patroldevice_code", patroldevice.getPatroldeviceCode()); + map.put("patroldevice_name", patroldevice.getPatroldeviceName()); + map.put("station_name", patroldevice.getStationName()); + map.put("station_code", patroldevice.getStationCode()); + map.put("device_model", patroldevice.getAreaId()); + map.put("manufacturer", patroldevice.getAreaName()); + map.put("use_unit", patroldevice.getUseUnit()); + map.put("device_source", patroldevice.getDeviceSource()); + map.put("production_date", patroldevice.getProductionDate()); + map.put("production_code", patroldevice.getProductionDate()); + map.put("istransport", patroldevice.getIstransport()); + map.put("use_mode", patroldevice.getUseMode()); + map.put("video_mode", patroldevice.getVideoMode()); + map.put("place", patroldevice.getPlace()); + map.put("type", patroldevice.getType()); + map.put("patroldevice_info", patroldevice.getPatroldeviceInfo()); + map.put("robots_code", patroldevice.getRobotsCode()); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "voca_model_ZzServer01"); +// filePath=String.format("%s_%s_%s.xml", "vocal", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Vocal_Model"); + break; + case "7": + List taskList = taskMapper.selectList(null); + for (Task task : taskList) { + Map map = new HashMap<>(); + map.put("station_name", task.getStationName()); + map.put("station_code", task.getStationCode()); + map.put("type", task.getType()); + map.put("task_code", task.getTaskCode()); + map.put("task_name", task.getTaskName()); + map.put("priority", task.getPriority()); + map.put("device_level", task.getDeviceLevel()); + map.put("device_list", task.getDeviceList()); + map.put("fixed_start_time", task.getFixedStartTime()); + map.put("cycle_month", task.getCycleMonth()); + map.put("cycle_week", task.getCycleWeek()); + map.put("cycle_execute_time", task.getCycleExecuteTime()); + map.put("cycle_start_time", task.getCycleStartTime()); + map.put("cycle_end_time", task.getCycleEndTime()); + map.put("interval_number", task.getIntervalNumber()); + map.put("interval_type", task.getIntervalType()); + map.put("interval_execute_time", task.getIntervalExecuteTime()); + map.put("interval_start_time", task.getIntervalStartTime()); + map.put("interval_end_time", task.getIntervalEndTime()); + map.put("invalid_start_time", task.getInvalidStartTime()); + map.put("invalid_end_time", task.getInvalidEndTime()); + map.put("isenable", task.getIsenable()); + map.put("creator", task.getCreator()); + map.put("create_time", task.getCreateTime()); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "task_model_ZzServer01"); +// filePath=String.format("%s_%s_%s.xml", "task", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Task_Model"); + break; + case "8": + List examinePlanList = examinePlanMapper.selectList(null); + for (ExaminePlan examinePlan : examinePlanList) { + Map map = new HashMap<>(); + map.put("station_name", examinePlan.getStationName()); + map.put("station_code", examinePlan.getStationCode()); + map.put("config_code", examinePlan.getConfigCode()); + map.put("enable", examinePlan.getEnable()); + map.put("start_time", examinePlan.getStartTime()); + map.put("end_time", examinePlan.getEndTime()); + map.put("device_level", examinePlan.getDeviceLevel()); + map.put("device_list", examinePlan.getDeviceList()); + map.put("coordinate_pixel", examinePlan.getCoordinatePixel()); + mapList.add(map); + } + filePath = String.format("%s/Model/%s.xml", stationCode, "overhaularea_model_ZzServer01"); +// filePath=String.format("%s_%s_%s.xml", "overhaularea", DateUtil.format(DateUtil.date(), "yyyyMMdd"), DateUtil.format(DateUtil.date(), "HHmmss")); + xmlPath = serverConfig.getModelPath() + filePath; + FileUtil.parseNodeToXML(mapList, xmlPath, "Overhaularea_Model"); + break; + case "9": + List substationList = + substationMapper.selectList(new LambdaQueryWrapper().eq(Substation::getDatastatus + , "1")); + Substation substation = substationList.get(0); + String images = substation.getImages(); + filePath = systempath + "station" + File.separator + images; + break; + } + return filePath; + } + + @Override + public void sendRobotModel(String robotId) { + SubstationPatroldevice substationPatroldevice = substationPatroldeviceMapper.selectById(robotId); + if (substationPatroldevice != null) { + sendMsgtoRobot(substationPatroldevice.getIpaddr(), serverConfig.getPatrolServerid(), substationPatroldevice.getCustom3(), SystemCode.TYPE_MODEL_SYNC_CODE.getCode(), SystemCode.COMMAND_MODEL_HOST_CODE.getCode(), substationPatroldevice.getStationCode(), ""); + } + } + + + private String getReturnCimFile(String stationcode, String monitorindex, String equip, String result, String time) { + StringBuffer content = new StringBuffer(); + content.append(String.format("\n", DateUtil.now())); + content.append("\n"); + content.append("@序号 站序号 监控索引号 设备名称 设备状态 事件时标\n"); + String resultinfo = String.format("#1\t%s\t%s\t%s\t%s\t%s\n", + stationcode, monitorindex, equip, result, time); + content.append(resultinfo); + content.append("\n"); + return content.toString(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotcomand/controller/RobotUdpController.java b/riis-system/src/main/java/com/yfd/platform/modules/robotcomand/controller/RobotUdpController.java new file mode 100644 index 0000000..c84c7c6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotcomand/controller/RobotUdpController.java @@ -0,0 +1,270 @@ +package com.yfd.platform.modules.robotcomand.controller; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationDevice; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.ISubstationDeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.robotcomand.service.RobotUdpService; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.HttpRESTfulUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * + * @Date: 2023/9/21 16:40 + * @Description: + */ +@RestController +@RequestMapping("/robot/udp") +@Api(value = "RobotUdpController", tags = "机器人指令发送") +@Slf4j +public class RobotUdpController { + + @Resource + private RobotUdpService robotUdpService; + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + @Resource + private HttpServerConfig httpServerConfig; + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + @Resource + private ISubstationDeviceService substationDeviceService; + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + @ApiOperation("控制机器人") + @GetMapping("/sendCommand") + public ResponseResult sendCommand(String command) { + String data = robotUdpService.sendSimple(command); + return ResponseResult.successData(data); + } + + @ApiOperation("测试字典") + @GetMapping("/test") + public ResponseResult test(String command) { + List> pictureDefectAnalysisTypeList = sysDictionaryItemsService.getDeviceByType( + "PictureDefectAnalysisType"); + Set customList = pictureDefectAnalysisTypeList.stream().filter(c -> ObjectUtil.isNotEmpty(c.get( + "custom1"))).map(c -> c.get("custom1").toString()).collect(Collectors.toSet()); + String value = ""; + for (String custom : customList) { + if (JSONUtil.isTypeJSONArray(custom)) { + cn.hutool.json.JSONArray jsonArray = JSONUtil.parseArray(custom); + for (int i = 0; i < jsonArray.size(); i++) { + cn.hutool.json.JSONObject jsonObject = jsonArray.getJSONObject(i); + // 检查当前对象的key字段是否与目标key匹配 + if (jsonObject.getStr("key").equals("bj_bpps")) { + // 如果匹配,取出对应的value字段的值 + value = jsonObject.getStr("value"); + break; // 找到后跳出循环 + } + } + } + + } + return ResponseResult.successData(value); + } + + @ApiOperation("发送机器人坐标") + @GetMapping("/sendMessage") + public void sendMessage(String coordinate_pixel, String coordinate_geography) throws InterruptedException { + // patroldevice_name string 巡视设备名称 + //patroldevice_code string 巡视设备编码 + //time string 时间 + //coordinate_pixel string 坐标 格式:”x,y,z,a”,x、y、z为地图文 + //件的坐标,a为巡视设备航向角 + //coordinate_geography s + int z = -6; + int x = 0; + for (int i = -80; i <= 69; i++) { + + Thread.sleep(1000); + if (i <= -40) { + x = i; + } else { + z = z - 3; + } + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("patroldevice_name", "机器狗"); + jsonObject.put("patroldevice_code", "0001"); + jsonObject.put("time", DateUtil.now()); + jsonObject.put("coordinate_pixel", x + "," + z); + jsonObject.put("coordinate_geography", x + "," + z); + jsonArray.add(jsonObject); + WebSocketServer.sendInfo("robot_status_data", jsonArray.toString()); + } + } + + @ApiOperation("退出机器人") + @GetMapping("/exitRobot") + public ResponseResult exitRobot(String command) { + String data = robotUdpService.exitRobotSession(command); + return ResponseResult.successData(data); + } + + @ApiOperation("登录机器人") + @PostMapping("/userLogin") + public ResponseResult userLogin(String Name, String Pass) { + String api = "cgi-bin/user?Operation=Login&Name=" + Name + "&Pass=" + Pass; + JSONObject jsonObject = httpRESTfulUtils.sendHttpPostStr("192.168.1.67", "80", api, null, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("获取设备当前色标") + @GetMapping("/getDMColor") + public ResponseResult getDMColor() { + String api = "cgi-bin/system?Module=DMColor"; + JSONObject jsonObject = httpRESTfulUtils.sendHttpGetStr("192.168.1.67", "80", api, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("设置设备当前色标") + @PostMapping("/setDMColor") + public ResponseResult setDMColor(String RuleId, String Status) { + String api = "cgi-bin/system?Module=DMColor&RuleId=" + RuleId + "&Status=" + Status; + JSONObject jsonObject = httpRESTfulUtils.sendHttpPostStr("192.168.1.67", "80", api, null, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("获取设备分辨率") + @GetMapping("/getDeviceResolution") + public ResponseResult getDeviceResolution() { + String api = "cgi-bin/system?Module=Query&Type=DM6xResolution"; + JSONObject jsonObject = httpRESTfulUtils.sendHttpGetStr("192.168.1.67", "80", api, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("设置点测温目标") + @PostMapping("/setPointTemperature") + public ResponseResult setPointTemperature(String Index, String Enable, String StartX, String StartY) { + String api = + "cgi-bin/system?Module=DMMeasure&Type=Point&Index=" + Index + "&Enable=" + Enable + "&StartX=" + StartX + "&StartY=" + StartY; + JSONObject jsonObject = httpRESTfulUtils.sendHttpPostStr("192.168.1.67", "80", api, null, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("设置区域测温目标") + @PostMapping("/setAreaTemperature") + public ResponseResult setAreaTemperature(String Index, String Enable, String StartX, String StartY, String EndX, + String EndY) { + String api = + "cgi-bin/system?Module=DMMeasure&Type=Area&Index=" + Index + "&Enable=" + Enable + "&StartX=" + StartX + "&StartY=" + StartY + "&EndX=" + EndX + "&EndY=" + EndY; + JSONObject jsonObject = httpRESTfulUtils.sendHttpPostStr("192.168.1.67", "80", api, null, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("测温数据获取") + @GetMapping("/getTemperatureData") + public ResponseResult getTemperatureData() { + String api = "cgi-bin/system?Module=DMTtl"; + JSONObject jsonObject = httpRESTfulUtils.sendHttpGetStr("192.168.1.67", "80", api, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("实时测温数据持续回传") + @GetMapping("/getRealTimeTemperature") + public ResponseResult getRealTimeTemperature() { + String api = "cgi-bin/alarm?Mode=UDP&Port=7200"; + JSONObject jsonObject = httpRESTfulUtils.sendHttpGetStr("192.168.1.67", "80", api, null); + return ResponseResult.successData(jsonObject); + } + + @ApiOperation("同步大华摄像机规则信息") + @GetMapping("/synchronizationRuleList") + public ResponseResult synchronizationRuleList(String ipaddr) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SubstationPatroldevice::getType, "10").in(SubstationPatroldevice::getVideoMode, "2", "3"); + if (StrUtil.isNotBlank(ipaddr)) { + queryWrapper.eq(SubstationPatroldevice::getIpaddr, ipaddr); + } + List list = substationPatroldeviceService.list(queryWrapper); + if (list.size() == 1) { + SubstationPatroldevice substationPatroldevice = list.get(0); + String api = "getThermometryRuleAll"; + if (StrUtil.isNotBlank(httpServerConfig.getDahuaApp())) { + api = httpServerConfig.getDahuaApp() + "/getThermometryRuleAll"; + } + Map param = new HashMap<>(); + param.put("ip", substationPatroldevice.getIpaddr()); + param.put("port", 37777); + param.put("username", "admin"); + param.put("password", "a1234567"); + param.put("channel", 1); + JSONObject jsonObject = httpRESTfulUtils.sendHttpPost("json", httpServerConfig.getSdkIp(), + httpServerConfig.getDahuaPort(), "", api + , param, null); + String code = jsonObject.getString("code"); + if (!"0".equals(code)) { + log.error("失败"); + return ResponseResult.error(); + } + String internationalId = substationPatroldevice.getInternationalId(); + List> deviceByJson = substationDeviceService.getDeviceByJson(internationalId); + log.info("成功"); + JSONArray data = jsonObject.getJSONArray("data"); + Map> ruleMap = + data.stream().collect(Collectors.groupingBy(d -> JSONUtil.parseObj(d).getStr("nPresetId"))); + for (Map map : deviceByJson) { + if (ObjectUtil.isEmpty(map.get("patroldevicePos"))) { + continue; + } + List list1 = ruleMap.get(map.get("patroldevicePos").toString()); + if (list1 != null && list1.size() > 0) { + cn.hutool.json.JSONObject jsonObject1 = JSONUtil.parseObj(list1.get(0)); + int nRuleId = jsonObject1.getInt("nRuleId"); + String szName = jsonObject1.getStr("szName"); + int nMeterType = jsonObject1.getInt("nMeterType"); + String type = "rectangle"; + if (1 == nMeterType) { + type = "point"; + } + if (2 == nMeterType) { + type = "line"; + } + cn.hutool.json.JSONArray coors = jsonObject1.getJSONArray("coors"); + cn.hutool.json.JSONObject outsideAngle = new cn.hutool.json.JSONObject(); + outsideAngle.putOnce("type", type); + outsideAngle.putOnce("ruleId", nRuleId); + outsideAngle.putOnce("ruleName", szName); + outsideAngle.putOnce("pos", coors); + String deviceId = map.get("deviceId").toString(); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SubstationDevice::getDeviceId, deviceId).set(SubstationDevice::getOutsideAngle, + outsideAngle.toString()); + substationDeviceService.update(updateWrapper); + } + } + + } + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/robotcomand/service/RobotUdpService.java b/riis-system/src/main/java/com/yfd/platform/modules/robotcomand/service/RobotUdpService.java new file mode 100644 index 0000000..3c2c47d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/robotcomand/service/RobotUdpService.java @@ -0,0 +1,91 @@ +package com.yfd.platform.modules.robotcomand.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +/** + * + * @Date: 2023/9/21 16:32 + * @Description: + */ +@Service +@Slf4j +public class RobotUdpService { + + private boolean online = false; + + // 每隔0.5秒发送心跳 + //@Scheduled(fixedRate = 500) + public void sendHeartbeat() { + String heartbeatCommand = "33"; + String response = this.sendAndReceive(heartbeatCommand); + // 处理机器人的响应,检查机器人是否在线 + // 处理机器人下线的情况 + online = "1".equals(response); + } + + + /********************************** + * 用途说明: 发送机器人指令 + * 参数说明 command + * 返回值说明: java.lang.String + ***********************************/ + public String sendSimple(String command) { + if (!online) { + return "机器人不在线"; + } + return sendAndReceive(command); + } + + /********************************** + * 用途说明: 退出机器人 + * 参数说明 command + * 返回值说明: java.lang.String + ***********************************/ + public String exitRobotSession(String command) { + return sendAndReceive(command); + } + + /********************************** + * 用途说明: 发送指令 + * 参数说明 command + * 返回值说明: java.lang.String + ***********************************/ + public String sendAndReceive(String command) { + try { + DatagramSocket socket = new DatagramSocket(); + + // 机器人的IP地址和端口号 + InetAddress robotAddress = InetAddress.getByName("192.168.1.120"); + // 机器人的实际端口号 + int robotPort = 43893; + + // 发送数据给机器人 + byte[] sendData = command.getBytes(); + DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, robotAddress, robotPort); + socket.send(sendPacket); + + // 接收机器人的响应 + byte[] receiveData = new byte[1024]; + DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); + socket.receive(receivePacket); + + // 处理机器人的响应数据 + String response = new String(receivePacket.getData(), 0, receivePacket.getLength()); + + // 关闭Socket + socket.close(); + + return response; + } catch (Exception e) { + log.error(e.getMessage());; + return "通信失败"; + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/test/controller/VoiceController.java b/riis-system/src/main/java/com/yfd/platform/modules/test/controller/VoiceController.java new file mode 100644 index 0000000..6d24fe2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/test/controller/VoiceController.java @@ -0,0 +1,125 @@ +package com.yfd.platform.modules.test.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.utils.HttpRESTfulUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @Date: 2024/4/17 18:14 + * @Description: + */ +@RestController +@RequestMapping("/") +@Api(value = "VoiceController", tags = "测试接口") +public class VoiceController { + + @Resource + private HttpRESTfulUtils httpRESTfulUtils; + + @PostMapping("/voiceprintDataCollect") + @ApiOperation("智能分析识别通知") + public ResponseResult voiceprintDataCollect(@RequestBody String params) { + + Map param = new HashMap<>(); + JSONObject jsonObject = JSONUtil.parseObj(params); + String requestId = jsonObject.getStr("requestId"); + JSONArray objectList = jsonObject.getJSONArray("objectList"); + JSONArray resultList = new JSONArray(); + for (int i = 0; i < objectList.size(); i++) { + JSONObject object = objectList.getJSONObject(i); + JSONObject result = new JSONObject(); + result.putOnce("objectId", object.getStr("objectId")); + result.putOnce("code", "2000"); + JSONArray results = new JSONArray(); + JSONObject file = new JSONObject(); + file.putOnce("filename", "voice.wav"); + file.putOnce("fileUrl", "http://192.168.1.20:8082/video/voice.wav"); + results.add(file); + result.putOnce("results", results); + resultList.add(result); + } + param.put("requestId", requestId); + param.put("resultList", resultList); + httpRESTfulUtils.sendHttpPost("json", "192.168.1.20", "8082", "", "voiceprintDataCollectRetNotify", param, + null); + + return ResponseResult.successData(params); + } + + @PostMapping("/voiceprintNnalyse") + @ApiOperation("智能分析识别通知") + public ResponseResult voiceprintNnalyse(@RequestBody String params) { + Map param = new HashMap<>(); + JSONObject jsonObject = JSONUtil.parseObj(params); + String requestId = jsonObject.getStr("requestId"); + JSONArray objectList = jsonObject.getJSONArray("objectList"); + JSONArray resultList = new JSONArray(); + for (int i = 0; i < objectList.size(); i++) { + JSONObject object = objectList.getJSONObject(i); + JSONArray typeList = object.getJSONArray("typeList"); + JSONObject resultObject = new JSONObject(); + JSONArray results = new JSONArray(); + for (int j = 0; j < typeList.size(); j++) { + JSONObject result = new JSONObject(); + String type = typeList.getStr(j); + result.putOnce("type", type); + result.putOnce("value", "1"); + result.putOnce("startTime", DateUtil.now()); + result.putOnce("endTime", DateUtil.now()); + result.putOnce("conf", "0.99"); + result.putOnce("desc", "局部放电" + j); + results.add(result); + } + resultObject.putOnce("objectId", object.getStr("objectId")); + resultObject.putOnce("code", "2000"); + resultObject.putOnce("results", results); + resultList.add(resultObject); + } + param.put("requestId", requestId); + param.put("resultList", resultList); + httpRESTfulUtils.sendHttpPost("json", "192.168.1.20", "8082", "", "voiceprintNnalyseRetNotify", param, + null); + return ResponseResult.successData(params); + } + + + @PostMapping("/sendMediaFileUrl") + @ApiOperation("机巢服务发送文件路径") + public ResponseResult sendMediaFileUrl(String jobId) { + // 创建一个固定大小的线程池 + ExecutorService executorService = Executors.newFixedThreadPool(28); + for (int i = 1; i <= 28; i++) { + int finalI = i; + executorService.submit(() -> { + int count = finalI % 10; + if (count == 0) { + count = 10; + } + String fileName = String.format("ssdfasdf_航点%s.jpeg", count); + Map param = new HashMap<>(10); + param.put("url", "http://192.168.1.20:8070/video/20240709174959.png"); + param.put("fileName", fileName); + param.put("jobId", jobId); + String api = "dock/task/sendMediaFileUrl"; + httpRESTfulUtils.sendHttpPost("json", "192.168.1.20", "8070", "", api, param, null); + }); + + } + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/modules/test/domain/Test.java b/riis-system/src/main/java/com/yfd/platform/modules/test/domain/Test.java new file mode 100644 index 0000000..e7fb840 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/modules/test/domain/Test.java @@ -0,0 +1,15 @@ +package com.yfd.platform.modules.test.domain; + +import lombok.Data; + +/** + * @Date: 2024/4/19 15:40 + * @Description: + */ +@Data +public class Test { + + private String requestHostIp; + private String requestHostPort; + private String requestId; +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/DataSourceController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/DataSourceController.java new file mode 100644 index 0000000..6b1094e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/DataSourceController.java @@ -0,0 +1,41 @@ +package com.yfd.platform.system.controller; + +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.datasource.DataSourceAspect; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * + * @since 2022-09-20 + */ +@RestController +@RequestMapping("/system") +@Api(tags = "切换数据库") +public class DataSourceController { + + @Resource + DataSourceAspect dataSourceAspect; + + /** + * 切换数据库 + * + * @DataSource(name="master") 可以通过注解方式切换数据库 + */ + @GetMapping("/changeDataSource") + @ApiOperation("切换数据库") + public ResponseResult changeDataSource(Integer type) { + if (type == null) { + return ResponseResult.error("参数为空"); + } + String dataBase = dataSourceAspect.getDataBase(type); + String mess = "已切换为" + dataBase + "数据库"; + return ResponseResult.success(mess); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/FtpclientController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/FtpclientController.java new file mode 100644 index 0000000..7e115b0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/FtpclientController.java @@ -0,0 +1,104 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.NumberUtil; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.utils.FtpClient; +import com.yfd.platform.utils.TLSFTPUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.io.File; + +/** + *

+ * 消息通知 前端控制器 + *

+ * + * @since 2023-03-19 + */ +@RestController +@RequestMapping("/ftp/client") +@Api(tags = "FTP客户端服务") +@Slf4j +public class FtpclientController { + @Resource + private FtpClient ftpClient; + @Resource + private TLSFTPUtils tlsftpUtils; + + @PostMapping("/uploadfile") + @ApiOperation("测试上传文件") + public ResponseResult uploadfile(String fileName, String remoteFileName) throws Exception { +// ftpClient.uploadFileNew(fileName, remoteFileName); + ftpClient.uploadFile(fileName, remoteFileName); + return ResponseResult.success(); + } + + @PostMapping("/uploadfileSystem") + @ApiOperation("服务器文件上传") + public ResponseResult uploadfileSystem(String ip, String port, String name, String password, String fileName, String remoteFileName, String status) throws Exception { + if (tlsftpUtils.isConnected()) { + log.info("ftp连接未关闭"); + try { + Thread.sleep(10000); + if (tlsftpUtils.isConnected()) { + log.info("ftp连接等待10秒后仍未关闭"); + tlsftpUtils.disconnect(); + } + } catch (InterruptedException e) { + log.error("ftp连接未关闭"); + log.error(e.getMessage());; + } + } + tlsftpUtils.connect(ip, name, password,NumberUtil.parseInt(port)); + try { + tlsftpUtils.upload(remoteFileName, new File(fileName)); + tlsftpUtils.disconnect(); + } catch (Exception e) { + tlsftpUtils.disconnect(); + } +// ftpClient.uploadfileSystem(ip, port, name, password, fileName, remoteFileName); + return ResponseResult.success(); + } + + + @PostMapping("/downloadFileSystem") + @ApiOperation("测试服务器下载") + public ResponseResult downloadFileSystem(String ip, String port, String name, String password, String localDir, String remoteFileName, String status) throws Exception { + if (tlsftpUtils.isConnected()) { + log.info("ftp连接未关闭"); + try { + Thread.sleep(10000); + if (tlsftpUtils.isConnected()) { + log.info("ftp连接等待10秒后仍未关闭"); + tlsftpUtils.disconnect(); + } + } catch (InterruptedException e) { + log.error("ftp连接未关闭"); + log.error(e.getMessage());; + } + } + tlsftpUtils.connect(ip, name, password, NumberUtil.parseInt(port)); + try { + tlsftpUtils.download(localDir, remoteFileName); + tlsftpUtils.disconnect(); + } catch (Exception e) { + tlsftpUtils.disconnect(); + } + // ftpClient.uploadfileSystem(ip, port, name, password, fileName, remoteFileName); + return ResponseResult.success(); + } + + @PostMapping("/downloadFile") + @ApiOperation("测试下在文件") + public ResponseResult downloadFile(String fileName, String remoteFileName) throws Exception { + ftpClient.downloadFile(fileName, remoteFileName); + return ResponseResult.success(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/LoginController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/LoginController.java new file mode 100644 index 0000000..bea2d88 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/LoginController.java @@ -0,0 +1,382 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.cache.impl.CacheObj; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.jwt.JWTUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.wf.captcha.base.Captcha; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.DatabaseConfigReader; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.config.TencentSmsProperties; +import com.yfd.platform.config.WebConfig; +import com.yfd.platform.config.bean.LoginCodeEnum; +import com.yfd.platform.config.bean.LoginProperties; +import com.yfd.platform.constant.Constant; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.system.service.ISysRoleService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.system.service.impl.UserDetailsServiceImpl; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.RequestHolder; +import com.yfd.platform.utils.StringUtils; +import com.yfd.platform.utils.sm3.SM3Utils; +import com.yfd.platform.utils.sm4.SM4Utils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + + +@RestController +@RequestMapping("/user") +@Api(value = "LoginController", tags = "用户登录") +public class LoginController { + + @Autowired + private WebConfig webConfig; + + @Resource + private IUserService userService; + + @Value("${rsa.private_key}") + private String privateKey; + + @Resource + private ISysLogService sysLogService; + + @Resource + private LoginProperties loginProperties; + + @Resource + private TencentSmsProperties smsProperties; + + @Resource + private ISysRoleService sysRoleService; + + @Resource + UserDetailsServiceImpl userDetailsService; + @Resource + private DatabaseConfigReader databaseConfigReader; + + @PostMapping("/login") + @ApiOperation("登录用户") + @ResponseBody + public ResponseResult login(SysUser user) throws Exception { + // 密码解密 + String password = SM4Utils.decrypt(user.getPassword(), privateKey); + + //用于开发测试,减少验证时间,正式使用应该改成true + boolean hascode = false; + if (hascode) { + // 查询验证码 + String code = webConfig.loginuserCache().get(user.getUuid()); + // 清除验证码 + webConfig.loginuserCache().remove(user.getUuid()); + if (StrUtil.isBlank(code)) { + return ResponseResult.error("图片验证码不存在或已过期!"); + } + if (StrUtil.isBlank(user.getCode()) || !user.getCode().equalsIgnoreCase(code)) { + return ResponseResult.error("图片验证码错误!"); + } + } + if (hascode && StrUtil.isNotBlank(user.getPhone())) { + // 短信登录 + String phone = user.getPhone(); + if (!Validator.isMobile(phone)) { + return ResponseResult.error("手机号不正确!"); + } + String key = Constant.USER_LOGIN + phone; + String verifyCode = webConfig.loginuserCache().get(key); + if (StrUtil.isBlank(verifyCode)) { + return ResponseResult.error("验证码不存在或已过期!"); + } + if (StrUtil.isBlank(user.getCode()) || !user.getCode().equalsIgnoreCase(verifyCode)) { + return ResponseResult.error("验证码错误!"); + } + } + + //检查用户名是否存在 + boolean isok = userDetailsService.checkUsername(user.getUsername()); + if (!isok) { + return ResponseResult.error("用户账号不存在!"); + } + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + String requestip = StringUtils.getIp(request); + String brower = StringUtils.getBrowser(request); + //用于开发测试,减少验证时间,正式使用应该改成true + boolean checkpassword = true; + if (checkpassword) { + //检查用户登录IP是否是合法的(设置允许的) + String loginip = StringUtils.getIp(request); + boolean islegalip = userDetailsService.checkUserLoginIp(loginip, user.getUsername()); + if (islegalip == false) { + return ResponseResult.error("用户登录IP不合法,请联系管理员!"); + } + + JSONObject checkinfo = userDetailsService.checkPassword(user.getUsername(), password, requestip, brower); + if (!checkinfo.getBool("isok")) { + //密码不正确,返回错误信息 + return ResponseResult.error(checkinfo.getStr("errorinfo")); + } + } + //获取登录信息 + LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(user.getUsername()); + SysUser user1 = loginUser.getUser(); + Integer status = user1.getStatus(); + String userId = loginUser.getUser().getId(); +// String loginStr = webConfig.loginuserCache().get("login:" + userId); +// if (StrUtil.isNotBlank(loginStr)) { +// return ResponseResult.message("3", userId); +// } + if (status == 0) { + return ResponseResult.error("用户已停用"); + } + if (status == 2) { + return ResponseResult.error("用户已锁定"); + } + + SysLog sysLog = new SysLog(); + sysLog.setUsercode(user.getUsername()); + sysLog.setUsername(loginUser.getUser().getNickname()); + sysLog.setRequestip(StringUtils.getIpAddr(request)); + sysLog.setBrowser(StringUtils.getBrowser(request)); + sysLog.setType("0"); + sysLog.setOpttype("登录(login)"); + sysLog.setModule("用户登录"); + String className = this.getClass().getName(); + String method = + Thread.currentThread().getStackTrace()[1].getMethodName(); + sysLog.setMethod(className + "." + method + "()"); + sysLog.setDescription(loginUser.getUser().getNickname() + "登录系统!"); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); + String content = "系统日志\t" + + user.getUsername() + "\t" + + loginUser.getUser().getNickname() + "\t" + + "用户登录\t" + + loginUser.getUser().getNickname() + "登录系统!\t" + + className + "." + method + "()" + "\t" + + StringUtils.getIpAddr(request) + "\t" + + StringUtils.getBrowser(request) + "\t" + + "登录(login)\t" + + DateUtil.now() + "\t\n"; + FileUtil.appendUtf8String(content, new File(databaseConfigReader.getBackupPath() + "sysLog.txt")); + Map map = new HashMap(10) { + private static final long serialVersionUID = 1L; + + { + put("userid", userId); + put("username", loginUser.getUsername()); + long expireTime = + System.currentTimeMillis() + (30L * 60L * 1000L);//30分钟无操作退出 + // long expireTime = + // System.currentTimeMillis() + ( 30L * 24L * 60L * 60L * 1000L); //30天 + put("expire_time", expireTime);//个月过期 + } + }; + + String token = JWTUtil.createToken(map, "12345678".getBytes()); + map.put("token", token); + //把完整的用户信息存入到HuTool缓存中,userId作为key + String jsonStr = JSONUtil.toJsonStr(loginUser); + webConfig.loginuserCache().put("login:" + userId, jsonStr); + webConfig.loginuserCache().put("expire_time:" + userId, map.get("expire_time").toString()); + Integer usertype = loginUser.getUser().getUsertype(); + if (usertype == 1) {// 非超级管理员 + List roleId = userService.getRoleId(loginUser.getUser().getId()); + if (roleId == null || roleId.size() == 0) { + return ResponseResult.error("您没有分配角色,不能登录系统"); + } else { + List list = sysRoleService.list(new LambdaQueryWrapper().in(SysRole::getId, roleId)); + long count = list.stream().filter(l -> "2".equals(l.getLevel()) || "4".equals(l.getLevel())).count(); + if (count > 0) { //系统管理和安全审计 + map.put("goto", "admin"); + } else { + long count1 = list.stream().filter(l -> "3".equals(l.getLevel())).count(); + if (count1 > 0) { //具有业务操作员角 + // 判断登录页面是否勾选管理后 + String flag = user.getCustom3(); + if ("1".equals(flag)) { + map.put("goto", "admin"); + } else { + map.put("goto", "business"); + } + } + } + } + } else { + String flag = user.getCustom3(); + if ("1".equals(flag)) { + map.put("goto", "admin"); + } else { + map.put("goto", "business"); + } + } + return ResponseResult.successData(map); + } + + @PostMapping("/loginCode") + @ApiOperation("发送登录验证码") + @ResponseBody + public ResponseResult loginCode(String phone) { + if (StrUtil.isBlank(phone)) { + return ResponseResult.error("请填写手机号"); + } + if (!Validator.isMobile(phone)) { + return ResponseResult.error("手机号格式不正确!"); + } + // String verifyCode = webConfig.loginuserCache().get(Constant.USER_LOGIN + phone); + // if (StrUtil.isNotBlank(verifyCode)) { + // return ResponseResult.error("验证码还在有效期内"); + // } + long codeL = System.nanoTime(); + String codeStr = Long.toString(codeL); + String code = codeStr.substring(codeStr.length() - 8, codeStr.length() - 2); + // 拼成唯一的key + // 将手机验证码设置到缓存,五分钟失效 + webConfig.loginuserCache().put(Constant.USER_LOGIN + phone, code, 1000 * 60 * 5); + String sessionContext = "登录验证码"; + String templateId = smsProperties.getTemplateIdMap().get("login"); + String[] templateParamSet = new String[1]; + templateParamSet[0] = code; + phone = "+86" + phone; + boolean ok = userService.sendVerCodeToPhone(phone, sessionContext, templateId, templateParamSet); + if (ok) { + return ResponseResult.success(); + } + return ResponseResult.error(); + } + + @ApiOperation("获取验证码") + @GetMapping(value = "/code") + public ResponseResult getCode() { + // 获取运算的结果 + Captcha captcha = loginProperties.getCaptcha(); + String uuid = Constant.CODE_KEY + IdUtil.simpleUUID(); + //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 + String captchaValue = captcha.text(); + if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() && captchaValue.contains(".")) { + captchaValue = captchaValue.split("\\.")[0]; + } + + // 将验证码放入缓存,设置失效时间为60秒 + webConfig.loginuserCache().put(uuid, captchaValue, Constant.CODE_EXPIRATION_TIME); + // 验证码信息 + Map imgResult = new HashMap(2) {{ + put("img", captcha.toBase64()); + put("uuid", uuid); + }}; + return ResponseResult.successData(imgResult); + } + + @PostMapping("/logout") + @ApiOperation("退出登录") + @ResponseBody + public ResponseResult logout() { + //获取SecurityContextHolder中的用户id + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication instanceof UsernamePasswordAuthenticationToken) { + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + String userId = loginuser.getUser().getId(); + //删除redis中的登陆用户信息 + webConfig.loginuserCache().remove("login:" + userId); + //记录退出日志 + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + SysLog sysLog = new SysLog(); + sysLog.setUsercode(loginuser.getUsername()); + sysLog.setUsername(loginuser.getUser().getNickname()); + sysLog.setRequestip(StringUtils.getIpAddr(request)); + sysLog.setBrowser(StringUtils.getBrowser(request)); + String className = this.getClass().getName(); + String method = + Thread.currentThread().getStackTrace()[1].getMethodName(); + sysLog.setMethod(className + "." + method + "()"); + sysLog.setType("0"); + sysLog.setOpttype("登出(logout)"); + sysLog.setModule("退出系统"); + sysLog.setDescription(loginuser.getUser().getNickname() + "注销退出系统!"); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); + String content = "系统日志\t" + + loginuser.getUsername() + "\t" + + loginuser.getUser().getNickname() + "\t" + + "退出系统\t" + + loginuser.getUser().getNickname() + "注销退出系统!\t" + + className + "." + method + "()" + "\t" + + StringUtils.getIpAddr(request) + "\t" + + StringUtils.getBrowser(request) + "\t" + + "登出(logout)\t" + + DateUtil.now() + "\t\n"; + + FileUtil.appendUtf8String(content, new File(databaseConfigReader.getBackupPath() + "sysLog.txt")); + } + + + return ResponseResult.success(); + } + + @GetMapping("/me") + @ApiOperation("查询当前用户信息") + @ResponseBody + public ResponseResult getUserInfo() { + ResponseResult responseResult = userService.getLoginUserInfo(); + if (responseResult == null) { + return ResponseResult.error("当前用户没有权限"); + } + return ResponseResult.successData(responseResult); + } + + @Log(module = "用户登录", value = "修改个人信息") + @PostMapping("/updatePersonalInfo") + @ApiOperation("修改个人信息") + @ResponseBody + public ResponseResult updateUser(@RequestBody SysUser user) { + if (StrUtil.isEmpty(user.getId())) { + return ResponseResult.error("没有用户ID"); + } + //填写 当前用户名称 + user.setLastmodifier(userService.getUsername()); + //填写 当前日期 + user.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok = userService.updateById(user); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + + @PostMapping("/kickOutUser") + @ApiOperation("踢出用户") + @ResponseBody + public ResponseResult kickOutUser(String userId) { + webConfig.loginuserCache().remove("login:" + userId); + return ResponseResult.success(); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/MessageController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/MessageController.java new file mode 100644 index 0000000..0864d3a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/MessageController.java @@ -0,0 +1,156 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.MessageConfig; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.util.*; + +/** + *

+ * 消息通知 前端控制器 + *

+ * + * + * @since 2023-03-19 + */ +@RestController +@RequestMapping("/system/message") +@Api(tags = "消息通知") +public class MessageController { + + @Resource + private IMessageService messageService; + + @Resource + private MessageConfig messageConfig; + + @ApiOperation("查询消息") + @GetMapping("/getMessageList") + public ResponseResult getMessageList(Page page, + String status, String title, + String type, String startDate, + String endDate) { + if (StrUtil.isBlank(status)) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if ("0".equals(status)) { + queryWrapper.eq(Message::getStatus, "1"); + } else { + List statusList = new ArrayList<>(); + statusList.add("2"); + statusList.add("9"); + queryWrapper.in(Message::getStatus, statusList); + + if (StrUtil.isNotBlank(title)) { + queryWrapper.like(Message::getTitle, title); + } + + if (StrUtil.isNotBlank(type)) { + queryWrapper.eq(Message::getType, type); + } + + DateTime parseStartDate = DateUtil.parse(startDate); + DateTime parseEndDate = DateUtil.parse(endDate); + DateTime dateTime = DateUtil.offsetDay(parseEndDate, 1); + + if (parseStartDate != null && parseEndDate != null) { + queryWrapper.ge(Message::getCreatetime, parseStartDate).lt(Message::getCreatetime, dateTime); + } + } + queryWrapper.orderByDesc(Message::getCreatetime); + Page pageList = messageService.page(page, queryWrapper); + return ResponseResult.successData(pageList); + } + + @ApiOperation("根据ID查询消息") + @GetMapping("/getMessageById") + public ResponseResult getMessageById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + Message message = messageService.getById(id); + Map map = new HashMap<>(); + map.put("title", message.getTitle()); + map.put("content", message.getContent()); + map.put("createtime", message.getCreatetime()); + return ResponseResult.successData(map); + } + + @Log(module = "消息通知", value = "根据ID删除消息",type = "0") + @ApiOperation("根据ID删除消息") + @PostMapping("/deleteMessageById") + public ResponseResult deleteMessageById(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] split = id.split(","); + List idList = Arrays.asList(split); + boolean ok = messageService.removeByIds(idList); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + + } + + @Log(module = "消息通知", value = "将消息标记为已阅状态",type = "0") + @ApiOperation("标记已阅") + @PostMapping("/setMessageStatus") + public ResponseResult setMessageStatus(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] split = id.split(","); + long time = System.currentTimeMillis(); + for (String mid : split) { + Message message = messageService.getById(mid); + if ("9".equals(message.getStatus())) { + continue; + } + message.setStatus("2"); + message.setReadtime(new Timestamp(time)); + messageService.updateById(message); + } + messageConfig.sendMessage(); + return ResponseResult.success(); + } + + @ApiOperation("全部已阅") + @PostMapping("/setAllMessageStatus") + public ResponseResult setAllMessageStatus() { + long time = System.currentTimeMillis(); + List list = + messageService.list(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + for (Message message : list) { + message.setStatus("2"); + message.setReadtime(new Timestamp(time)); + messageService.updateById(message); + } + messageConfig.sendMessage(); + return ResponseResult.success(); + } + + @ApiOperation("查看未读的消息数量") + @GetMapping("/getMessageCount") + public ResponseResult getMessageCount() { + long count = + messageService.count(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + return ResponseResult.successData(count); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/QuartzJobController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/QuartzJobController.java new file mode 100644 index 0000000..fa90039 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/QuartzJobController.java @@ -0,0 +1,183 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.service.IQuartzJobService; +import com.yfd.platform.system.service.impl.UserServiceImpl; +import com.yfd.platform.utils.QuartzManage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.quartz.CronExpression; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + *

+ * 定时任务 前端控制器 + *

+ * + * + * @since 2023-03-19 + */ +@RestController +@RequestMapping("/system/quartzjob") +@Api(tags = "定时任务") +@Transactional +public class QuartzJobController { + + @Resource + private IQuartzJobService quartzJobService; + + @Resource + private UserServiceImpl currentUser; + + @Resource + private QuartzManage quartzManage; + + @ApiOperation("查询定时任务") + @GetMapping("/getQuartzJobList") + public ResponseResult getQuartzJobList(Page page, + String jobName) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(jobName)) { + queryWrapper.like(QuartzJob::getJobName, jobName); + } + queryWrapper.orderByAsc(QuartzJob::getOrderno); + Page pageList = quartzJobService.page(page, queryWrapper); + return ResponseResult.successData(pageList); + } + + @Log(module = "定时任务管理", value = "新增定时任务",type = "0") + @ApiOperation("新增定时任务") + @PostMapping("/addQuartzJob") + public ResponseResult addQuartzJob(@RequestBody QuartzJob quartzJob) { + if (quartzJob == null) { + return ResponseResult.error("参数为空"); + } + // 添加最近修改人 + quartzJob.setLastmodifier(currentUser.getUsername()); + // 添加最近修改时间 + quartzJob.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + if (StrUtil.isBlank(quartzJob.getJobCron()) || !CronExpression.isValidExpression(quartzJob.getJobCron())) { + return ResponseResult.error("cron表达式格式错误"); + } + quartzJob.setStatus("0"); + boolean ok = quartzJobService.addQuartzJob(quartzJob); + quartzManage.addJob(quartzJob); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("新增失败"); + } + } + + @Log(module = "定时任务管理", value = "设置定时任务是否有效",type = "0") + @ApiOperation("设置定时任务是否有效") + @PostMapping("/setQuartzStatus") + public ResponseResult setQuartzStatus(@RequestParam String id, + @RequestParam String status) { + if (StrUtil.isBlank(id) || StrUtil.isBlank(status)) { + return ResponseResult.error("参数为空"); + } + LambdaUpdateWrapper updateWrapper = + new LambdaUpdateWrapper<>(); + //根据id 更新状态,最近修改人,最近修改时间 + updateWrapper.eq(QuartzJob::getId, id).set(QuartzJob::getStatus, + status).set( + QuartzJob::getLastmodifier, currentUser.getUsername()).set(QuartzJob::getLastmodifydate, + LocalDateTime.now()); + boolean ok = quartzJobService.update(updateWrapper); + QuartzJob quartzJob = quartzJobService.getById(id); + if ("0".equals(quartzJob.getStatus())) { + quartzManage.pauseJob(quartzJob); + } else { + quartzManage.resumeJob(quartzJob); + } + + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @ApiOperation("根据ID查询定时任务") + @GetMapping("/getQuartzJobById") + public ResponseResult getQuartzJobById(String id) { + QuartzJob quartzJob = quartzJobService.getById(id); + return ResponseResult.successData(quartzJob); + } + + @Log(module = "定时任务管理", value = "修改定时任务",type = "0") + @ApiOperation("修改定时任务") + @PostMapping("/updateQuartzJob") + @Transactional(rollbackFor = Exception.class) + public ResponseResult updateQuartzJob(@RequestBody QuartzJob quartzJob) { + // 添加最近修改人 + quartzJob.setLastmodifier(currentUser.getUsername()); + // 添加最近修改时间 + quartzJob.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + if (StrUtil.isBlank(quartzJob.getJobCron()) || !CronExpression.isValidExpression(quartzJob.getJobCron())) { + return ResponseResult.error("cron表达式格式错误"); + } + boolean ok = quartzJobService.updateById(quartzJob); + quartzManage.updateJobCron(quartzJob); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("修改失败"); + } + } + + @Log(module = "定时任务管理", value = "删除定时任务",type = "0") + @ApiOperation("删除定时任务") + @PostMapping("/deleteQuartzJob") + public ResponseResult deleteQuartzJob(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = quartzJobService.deleteQuartzJob(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + + @Log(module = "定时任务管理", value = "执行定时任务",type = "0") + @ApiOperation("执行定时任务") + @PostMapping("/execution") + public ResponseResult execution(@RequestParam String id) { + quartzJobService.execution(quartzJobService.getById(id)); + return ResponseResult.success(); + } + + /********************************** + * 用途说明: 拖动修改定时顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 成功或者失败 + ***********************************/ + @Log(module = "定时任务管理", value = "拖动定时任务",type = "0") + @PostMapping("/changeDictOrder") + @ApiOperation("拖动修改定时任务顺序") + public ResponseResult changeQuartzOrder(@RequestParam String fromID, + @RequestParam String toID) { + + boolean ok = quartzJobService.changeDictOrder(fromID, toID); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/RolePatroldeviceController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/RolePatroldeviceController.java new file mode 100644 index 0000000..0758f74 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/RolePatroldeviceController.java @@ -0,0 +1,18 @@ +package com.yfd.platform.system.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 系统角色摄像头对照 前端控制器 + *

+ * + * + * @since 2023-04-23 + */ +@RestController +@RequestMapping("/system/role-patroldevice") +public class RolePatroldeviceController { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SSEController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SSEController.java new file mode 100644 index 0000000..bbe7eb9 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SSEController.java @@ -0,0 +1,50 @@ +package com.yfd.platform.system.controller; + +import com.yfd.platform.component.ServerSendEventServer; +import com.yfd.platform.component.WebSocketServer; +import com.yfd.platform.system.service.IMessageService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.annotation.Resource; + + +@Slf4j +@RestController +@CrossOrigin +@RequestMapping("/sse") +@Api(tags = "SSE推送服务") +public class SSEController { + + @Resource + private IMessageService messageService; + + @GetMapping("/connect/{token}") + @ApiOperation("建立连接") + public SseEmitter connect(@PathVariable String token) { + SseEmitter connect = ServerSendEventServer.connect(token); + return connect; + } + + @GetMapping("/sendmsg") + @ApiOperation("发送消息") + public void sendMessage(String token, String message) throws InterruptedException { + + ServerSendEventServer.sendMessage(token, message); + } + + @GetMapping("/sendgroupmsg") + @ApiOperation("多人发送消息") + public void sendgroupmsg(String groupid, String message) throws InterruptedException { + WebSocketServer.sendInfo(groupid, message); + } + + @GetMapping("/disconnect/{token}") + @ApiOperation("关闭连接") + public void disconnect(@PathVariable String token) throws InterruptedException { + ServerSendEventServer.removeUser(token); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysConfigController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysConfigController.java new file mode 100644 index 0000000..453dd4c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysConfigController.java @@ -0,0 +1,142 @@ +package com.yfd.platform.system.controller; + + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.system.domain.SysConfig; +import com.yfd.platform.system.service.ISysConfigService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.sql.Timestamp; + + +/** + *

+ * 系统全局配置 前端控制器 + *

+ * + * + * @since 2022-01-19 + */ +@RestController +@RequestMapping("/system/config") +@Api(tags = "系统全局配置") +public class SysConfigController { + @Resource + private ISysConfigService configService; + + @Resource + private IUserService userService; + + @PostMapping("/getOneById") + @ApiOperation("根据id查询全局配置详情记录") + @ResponseBody + public SysConfig getOneById(String id) { + return configService.getOne(null); + } + + @PostMapping("/addConfig") + @ApiOperation("根据id查询全局配置详情记录") + @ResponseBody + public ResponseResult addConfig(@RequestBody SysConfig config) throws IOException, UnsupportedAudioFileException { + if (StrUtil.isEmpty(config.getId())) { + config.setId(IdUtil.fastSimpleUUID()); + } + + config.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok = configService.save(config); + return ResponseResult.success(); + } + + @PostMapping("/updateById") + @ApiOperation("根据id修改全局配置记录") + @ResponseBody + public ResponseResult updateById(@RequestBody SysConfig config) { + config.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok = configService.saveOrUpdate(config); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("操作失败!"); + } + } + + @Log(module = "系统全局配置", value = "上传系统操作手册", type = "0") + @PostMapping("/uploadConfigFile") + @ApiOperation("上传系统操作手册") + @ResponseBody + public ResponseResult uploadConfigFile(MultipartFile file) throws FileNotFoundException { + if (file.isEmpty()) { + return ResponseResult.error("文件是空的"); + } + String filename = file.getOriginalFilename(); + String suffix = StrUtil.subAfter(filename, ".", true).toLowerCase(); + if (!"pdf".equals(suffix)) { + return ResponseResult.error("非法文件类型"); + } + String contentType = file.getContentType(); + if (!"application/pdf".equals(contentType)) { + return ResponseResult.error("非法文件内容"); + } + configService.uploadModelFile(file); + return ResponseResult.success(); + } + + @Log(module = "系统全局配置", value = "下载操作手册", type = "0") + @PostMapping("/downloadConfigFile") + @ApiOperation("下载操作手册") + public ResponseResult downloadConfigFile(String fileName, HttpServletResponse response) throws FileNotFoundException { + if (StrUtil.isBlank(fileName)) { + return ResponseResult.error("文件名是空的"); + } + if (fileName.contains("\\") || fileName.contains("/")) { + return ResponseResult.error("非法文件路径"); + } + String suffix = StrUtil.subAfter(fileName, ".", true).toLowerCase(); + if (!"pdf".equals(suffix)) { + return ResponseResult.error("非法文件类型"); + } + + configService.downloadConfigFile(fileName, response); + return ResponseResult.success("下载成功"); + } + + @PostMapping("/deleteConfigFile") + @ApiOperation("删除文件") + public ResponseResult deleteConfigFile(String id, String fileName) { + if (StrUtil.isBlank(fileName)) { + throw new RuntimeException("文件名是空的"); + } + if (fileName.contains("\\") || fileName.contains("/")) { + throw new RuntimeException("非法文件路径"); + } + String suffix = StrUtil.subAfter(fileName, ".", true).toLowerCase(); + if (!"pdf".equals(suffix)) { + throw new RuntimeException("非法文件类型"); + } + int count = configService.count(new LambdaQueryWrapper().eq(SysConfig::getId, id).eq(SysConfig::getId, fileName)); + if (count < 0) { + return ResponseResult.error("删除失败"); + } + boolean ok = configService.deleteConfigFile(id, fileName); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysDataBackupController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysDataBackupController.java new file mode 100644 index 0000000..76712ac --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysDataBackupController.java @@ -0,0 +1,107 @@ +package com.yfd.platform.system.controller; + + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysDataBackup; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.service.ISysDataBackupService; +import com.yfd.platform.system.service.ISysDictionaryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; + +/** + *

+ * 系统数据备份恢复记录 前端控制器 + *

+ * + * + * @since 2023-08-31 + */ +@RestController +@RequestMapping("/system/databackup") +@Api(tags = "数据备份恢复") +public class SysDataBackupController { + + @Resource + private ISysDataBackupService dataBackupService; + + /********************************** + * 用途说明: 备份数据库表 + * 参数说明 datascope 类型 全部数据 基础数据 业务数据 + * 返回值说明: boolean 是否成功 + ***********************************/ + @PostMapping("/backupDataTable") + @ApiOperation("备份数据表") + public ResponseResult backupDataTable(String datascope) { + boolean ok = dataBackupService.backupDataTable(datascope, "手工备份"); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 备份数据库表 + * 参数说明 id 数据表备份文件记录id + * 返回值说明: boolean 是否成功 + ***********************************/ + @PostMapping("/recoverDataTable") + @ApiOperation("恢复数据表") + public ResponseResult recoverDataTable(String id) { + boolean ok = dataBackupService.recoverDataTable(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 page分页对象、datascope(数据范围)、backuptype(backuptype) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明:返回分页查询结果 + ***********************************/ + @GetMapping("/getBackFileList") + @ApiOperation("分页查询备份文件信息") + public ResponseResult getBackFileList(String filename, String datascope, String backuptype, String startDate, + String endDate, Page page) { + + Page sysDataBackupPage = dataBackupService.getBackFileList(filename, datascope, backuptype, + startDate, endDate, page); + Map map = new HashMap<>(); + map.put("list", sysDataBackupPage.getRecords()); + map.put("total", sysDataBackupPage.getTotal()); + map.put("size", sysDataBackupPage.getSize()); + map.put("current", sysDataBackupPage.getCurrent()); + return ResponseResult.successData(map); + } + + /********************************** + * 用途说明: 下载数据库文件 + * 参数说明 id 数据表备份文件记录id + * 返回值说明: boolean 是否成功 + ***********************************/ + @PostMapping("/downloadDataTable") + @ApiOperation("下载数据库文件") + public ResponseResult downloadDataTable(String id, HttpServletResponse response) { + boolean ok = dataBackupService.downloadDataTable(id, response); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java new file mode 100644 index 0000000..d884c55 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java @@ -0,0 +1,152 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.service.ISysDictionaryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 数据字典表 前端控制器 + *

+ * + * + * @since 2023-03-08 + */ +@RestController +@RequestMapping("/system/dictionary") +@Api(tags = "数据字典") +public class SysDictionaryController { + + @Resource + private ISysDictionaryService sysDictionaryService; + + /********************************** + * 用途说明: 获取数据字典列表 + * 参数说明 dictType 字典类型 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @GetMapping("/dictList") + @ApiOperation("获取数据字典列表") + public ResponseResult getDictList(String dictType) { + if (StrUtil.isBlank(dictType)) { + return ResponseResult.error("参数为空"); + } + List sysDictionaries = + sysDictionaryService.getDictList(dictType); + return ResponseResult.successData(sysDictionaries); + } + + /********************************** + * 用途说明: 根据ID删除字典 + * 参数说明 id 字典ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除结果成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "根据ID删除字典",type = "0") + @PostMapping("/deleteById") + @ApiOperation("根据ID删除字典") + public ResponseResult deleteDictById(@RequestParam String id) { + boolean ok = sysDictionaryService.deleteDictById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 新增字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "新增数据字典",type = "0") + @PostMapping("/addDict") + @ApiOperation("新增字典") + public ResponseResult addDict(@RequestBody SysDictionary sysDictionary) { + if (sysDictionary == null) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionary::getDictCode, sysDictionary.getDictCode()).or().eq(SysDictionary::getDictName, sysDictionary.getDictName()); + int count = sysDictionaryService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前字典编号或名称已经存在!"); + } + boolean ok = sysDictionaryService.addDict(sysDictionary); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 修改字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "修改数据字典",type = "0") + @PostMapping("/updateDict") + @ApiOperation("修改字典") + public ResponseResult updateDict(@RequestBody SysDictionary sysDictionary) { + if (sysDictionary == null) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(SysDictionary::getId,sysDictionary.getId()).and(i->i.eq(SysDictionary::getDictCode, sysDictionary.getDictCode()).or().eq(SysDictionary::getDictName, sysDictionary.getDictName())); + int count = sysDictionaryService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前字典编号或名称已经存在!"); + } + boolean ok = sysDictionaryService.updateById(sysDictionary); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 根据ID查询字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回查询结果 + ***********************************/ + @PostMapping("/getDictById") + @ApiOperation("根据ID查询字典") + public ResponseResult getDictById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + SysDictionary sysDictionary = sysDictionaryService.getById(id); + return ResponseResult.successData(sysDictionary); + } + + /********************************** + * 用途说明: 拖动修改字典顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Log(module = "数据字典", value = "拖动修改字典顺序",type = "0") + @PostMapping("/changeDictOrder") + @ApiOperation("拖动修改字典顺序") + public ResponseResult changeDictOrder(@RequestParam String fromID, + @RequestParam String toID) { + + boolean ok = sysDictionaryService.changeDictOrder(fromID, toID); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java new file mode 100644 index 0000000..931037d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java @@ -0,0 +1,227 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.system.service.ISysDictionaryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + *

+ * 数据字典明细 前端控制器 + *

+ * + * + * @since 2023-03-08 + */ +@RestController +@RequestMapping("/system/dictionaryItems") +@Api(tags = "数据字典项") +public class SysDictionaryItemsController { + + @Resource + private ISysDictionaryItemsService sysDictionaryItemsService; + + @Resource + private ISysDictionaryService sysDictionaryService; + + /********************************** + * 用途说明: 分页查询字典项信息 + * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @GetMapping("/page") + @ApiOperation("分页查询字典项信息") + public ResponseResult getDictItemPage(String dictId, String dictName, + Page page) { + + Page sysDictionaryItemsPage = + sysDictionaryItemsService.getDictItemPage(dictId, dictName, + page); + return ResponseResult.successData(sysDictionaryItemsPage); + } + + /********************************** + * 用途说明: 增加字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "增加字典项",type = "0") + @PostMapping("/addDictionaryItem") + @ApiOperation("增加字典项") + public ResponseResult addDictionaryItem(@RequestBody SysDictionaryItems sysDictionaryItems) { + if (sysDictionaryItems == null) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId,sysDictionaryItems.getDictId()).and(i -> i.eq(SysDictionaryItems::getItemCode, sysDictionaryItems.getItemCode()).or().eq(SysDictionaryItems::getDictName, sysDictionaryItems.getDictName())); + int count = sysDictionaryItemsService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前字典项编号或名称已经存在!"); + } + boolean ok = + sysDictionaryItemsService.addDictionaryItem(sysDictionaryItems); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 修改字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回修改成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "修改字典项",type = "0") + @PostMapping("/updateDictionaryItem") + @ApiOperation("修改字典项") + public ResponseResult updateDictionaryItem(@RequestBody SysDictionaryItems sysDictionaryItems) { + if (sysDictionaryItems == null) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId,sysDictionaryItems.getDictId()).ne(SysDictionaryItems::getId, sysDictionaryItems.getId()).and(i -> i.eq(SysDictionaryItems::getItemCode, sysDictionaryItems.getItemCode()).or().eq(SysDictionaryItems::getDictName, sysDictionaryItems.getDictName())); + int count = sysDictionaryItemsService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前字典项编号或名称已经存在!"); + } + boolean ok = + sysDictionaryItemsService.updateById(sysDictionaryItems); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 根据ID查询字典项 + * 参数说明 id 字典项ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回字典项信息 + ***********************************/ + @GetMapping("/getDictItemById") + @ApiOperation("根据ID查询字典项") + public ResponseResult getDictItemById(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + SysDictionaryItems sysDictionaryItems = + sysDictionaryItemsService.getById(id); + return ResponseResult.successData(sysDictionaryItems); + } + + @GetMapping("/getDictItemByCode") + @ApiOperation("根据父Code查询字典项") + public ResponseResult getDictItemByCode(String code) { + if (StrUtil.isBlank(code)) { + return ResponseResult.error("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getParentCode, code); + List list = sysDictionaryItemsService.list(queryWrapper); + return ResponseResult.successData(list); + } + + /********************************** + * 用途说明: 根据ID删除字典项 + * 参数说明 id 字典项ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "根据ID删除字典项",type = "0") + @PostMapping("/deleteDictItemById") + @ApiOperation("根据ID删除字典项") + public ResponseResult deleteDictItemById(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = sysDictionaryItemsService.removeById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 批量删除字典项 + * 参数说明 ids 字典项id数组 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回批量删除成功或失败 + ***********************************/ + @Log(module = "数据字典项", value = "批量删除字典项",type = "0") + @PostMapping("/deleteDictItemByIds") + @ApiOperation("批量删除字典项") + public ResponseResult deleteDictItemByIds(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + String[] splitIds = id.split(","); + // 数组转集合 + List ids = Arrays.asList(splitIds); + boolean ok = sysDictionaryItemsService.removeByIds(ids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 拖动修改字典项顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Log(module = "数据字典项", value = "拖动修改字典项顺序",type = "0") + @PostMapping("/changeItemOrder") + @ApiOperation("拖动修改字典项顺序") + public ResponseResult changeItemOrder(@RequestParam String fromID, + @RequestParam String toID) { + boolean ok = sysDictionaryItemsService.changeItemOrder(fromID, toID); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + @GetMapping("/getDeviceByType") + @ApiOperation("获取类型") + public ResponseResult getDeviceByType(String dictcode) { + if (StrUtil.isBlank(dictcode)) { + return ResponseResult.error("参数为空"); + } + List> itemCode = sysDictionaryItemsService.getDeviceByType(dictcode); + return ResponseResult.successData(itemCode); + } + + /********************************** + * 用途说明: 导出数据字典项数据 + * 参数说明 sysDictionaryItemsList 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或失败 + ***********************************/ + @Log(module = "数据字典项", value = "导出字典数据到Excel",type = "0") + @GetMapping("/exportExcel") + @ApiOperation("导出数据字典项数据") + public void exportExcel(String dictID, String itemName, + Page page, + HttpServletResponse response) { + Page sysDictionaryItemsPage = + sysDictionaryItemsService.getDictItemPage(dictID, itemName, + page); + sysDictionaryItemsService.exportExcel(sysDictionaryItemsPage.getRecords(), response); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysLogController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysLogController.java new file mode 100644 index 0000000..742c204 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysLogController.java @@ -0,0 +1,100 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.service.ISysLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统操作日志 前端控制器 + *

+ * + * + * @since 2023-03-08 + */ +@RestController +@RequestMapping("/system/log") +@Api(tags = "系统日志") +public class SysLogController { + + @Resource + private ISysLogService sysLogService; + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 page分页对象、username(用户名)、(optType) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @PostMapping("/getLogList") + @ApiOperation("分页查询日志信息") + public ResponseResult getLogList(String username, String optType, String requestip, String module,String type, + String startDate, String endDate, String orderfield, Page page) { + + Page sysLogPage = sysLogService.getLogList(username, optType, requestip, module,type, + startDate, endDate, orderfield, page); + Map map = new HashMap<>(); + List records = sysLogPage.getRecords(); + records.forEach(r -> { + String params = r.getParams(); + if (JSONUtil.isTypeJSONObject(params)) { + JSONObject jsonObject = JSONUtil.parseObj(params); + jsonObject.remove("phone"); + jsonObject.remove("contactPhone"); + r.setParams(jsonObject.toString()); + } + }); + map.put("list", records); + map.put("total", sysLogPage.getTotal()); + map.put("size", sysLogPage.getSize()); + map.put("current", sysLogPage.getCurrent()); + return ResponseResult.successData(map); + } + + /********************************** + * 用途说明: 导出日志数据 + * 参数说明 sysLogs 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + @Log(module = "系统日志", value = "导出系统日志数",type = "0") + @GetMapping("/exportExcel") + @ApiOperation("导出日志数据") + public void exportExcel(String username, String optType, String requestip, String module,String type, + String startDate, + String endDate, String orderfield, Page page, + HttpServletResponse response) throws IOException { + sysLogService.exportExcel(username, optType, requestip, module,type, + startDate, endDate, orderfield, response); + } + + /********************************** + * 用途说明: 删除6月前数据 + * 参数说明 无 + * 返回值说明: + ***********************************/ + @Log(module = "系统日志", value = "删除6月前数据",type = "0") + @GetMapping("/deleteLogs") + @ApiOperation("删除6月前数据") + public ResponseResult deleteLogs() { + sysLogService.deleteLogs(); + return ResponseResult.success(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysMenuController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysMenuController.java new file mode 100644 index 0000000..0ce1215 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysMenuController.java @@ -0,0 +1,343 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysMenu; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysMenuService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +/** + *

+ * 菜单及按钮 前端控制器 + *

+ * + * + * @since 2021-12-15 + */ +@RestController +@RequestMapping("/system/menu") +@Api(value = "SysMenuController", tags = "菜单及按钮") +public class SysMenuController { + + @Resource + private ISysMenuService sysMenuService; + + @Resource + private IUserService userService; + + //菜单图片路径 + @Value("${file-space.system}") + private String systempath; + + /*********************************** + * 用途说明:获取菜单结构树(含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @PostMapping("/getMenuButtonTree") + @ApiOperation("获取菜单结构树(含按钮)") + @ResponseBody + @PreAuthorize("@el.check('select:menu')") + public List> getMenuButtonTree(String systemcode, + String name, + String isdisplay) { + return sysMenuService.getMenuButtonTree(systemcode, name, isdisplay); + } + + /*********************************** + * 用途说明:获取菜单结构树(不含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @PostMapping("/getMenuTree") + @ApiOperation("获取菜单结构树(不含按钮)") + @ResponseBody + public List> getMenuTree(String systemcode, + String name, + String isdisplay) { + return sysMenuService.getMenuTree(systemcode, name, isdisplay); + } + + /*********************************** + * 用途说明:权限分配 + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @PostMapping("/permissionAssignment") + @ApiOperation("获取分配权限(不含按钮)") + @ResponseBody + public List> permissionAssignment(String code, String roleId) { + if (StrUtil.isBlank(code)) { + code = "1"; + } + return sysMenuService.permissionAssignment(code, roleId); + } + + /********************************** + * 用途说明: 获取当前用户菜单结构树 + * 参数说明 + * 返回值说明: java.util.List + ***********************************/ + @GetMapping("/treeRoutes") + @ApiOperation("获取当前用户菜单结构树") + @ResponseBody + public List> getMenuTreeByUser() { + SysUser userInfo = userService.getUserInfo(); + String id = ""; + if (0 != userInfo.getUsertype()) { + id = userInfo.getId(); + } + return sysMenuService.getMenuTree(id); + } + + /********************************** + * 用途说明: 获取当前用户菜单结构树 + * 参数说明 + * 返回值说明: java.util.List + ***********************************/ + @GetMapping("/getMenuTreeByType") + @ApiOperation("根据系统类型获取用户菜单结构树") + @ResponseBody + public List> getMenuTreeByType(String sysetmCode) { + SysUser userInfo = userService.getUserInfo(); + String id = ""; + if (0 != userInfo.getUsertype()) { + id = userInfo.getId(); + } + return sysMenuService.getMenuTreeByType(id, sysetmCode); + } + + /*********************************** + * 用途说明:根据id查询菜单或按钮详情 + * 参数说明 + * id 菜单或按钮表id + * 返回值说明: 菜单或按钮表对象 + ***********************************/ + @PostMapping("/getOneById") + @ApiOperation("根据id查询菜单或按钮详情") + @ResponseBody + public ResponseResult getOneById(String id) { + SysMenu sysMenu = sysMenuService.getById(id); + return ResponseResult.successData(sysMenu); + } + + /*********************************** + * 用途说明:新增菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否添加成功提示 + ***********************************/ + @Log(module = "菜单及按钮", value = "新增菜单及按钮!",type = "0") + @PostMapping("/addMenu") + @ApiOperation("新增菜单及按钮") + @ResponseBody + public ResponseResult addMenu(@RequestBody SysMenu sysMenu) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getSystemcode, sysMenu.getSystemcode()).eq(SysMenu::getType, sysMenu.getType()).eq(SysMenu::getName, sysMenu.getName()); + int count = sysMenuService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前菜单或按钮已经存在!"); + } + boolean isOk = sysMenuService.addMenu(sysMenu); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否修改成功提示 + ***********************************/ + @Log(module = "菜单及按钮", value = "修改菜单及按钮",type = "0") + @PostMapping("/updateById") + @ApiOperation("修改菜单及按钮") + @ResponseBody + public ResponseResult updateById(@RequestBody SysMenu sysMenu) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(SysMenu::getId, sysMenu.getId()).eq(SysMenu::getSystemcode, sysMenu.getSystemcode()).eq(SysMenu::getType, sysMenu.getType()).eq(SysMenu::getName, sysMenu.getName()); + int count = sysMenuService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前菜单或按钮已经存在!"); + } + sysMenu.setLastmodifier(userService.getUsername()); + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean isOk = sysMenuService.updateById(sysMenu); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除单个图标 + * 参数说明 + * id 删除图标id + * icon 图标名称 + * 返回值说明: 是否删除成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "根据id删除单个图标!",type = "0") + @PostMapping("/deleteIcon") + @ApiOperation("根据id删除单个图标") + @ResponseBody + public ResponseResult deleteIcon(@RequestParam String id) { + boolean ok = sysMenuService.deleteIcon(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:更新菜单及按钮是否有效 + * 参数说明 + * id 菜单及按钮表id + * isdisplay 是否有效字段 + * 返回值说明: 是否更新成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "更新菜单及按钮是否有效!",type = "0") + @PostMapping("/setIsDisplay") + @ApiOperation("更新菜单及按钮是否有效") + @ResponseBody + public ResponseResult setIsDisplay(String id, String isdisplay) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 修改是否显示 ,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("isdisplay", isdisplay).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", + new Timestamp(System.currentTimeMillis())); + boolean ok = sysMenuService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:菜单及按钮序号排序 + * 参数说明 + * parentid 上级id + * orderMap map<菜单及按钮表id,排列序号> + * 返回值说明: 是否更新成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "菜单及按钮序号排序!",type = "0") + @PostMapping("/moveOrderno") + @ApiOperation("菜单及按钮序号排序") + @ResponseBody + public ResponseResult moveOrderno(@RequestParam String parentid, + @RequestParam String id, + @RequestParam int orderno) { + boolean ok = sysMenuService.moveOrderno(parentid, id, orderno); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除菜单或按钮 + * 参数说明 + * id 删除列的id + * 返回值说明: 是否删除成功 + ***********************************/ + @Log(module = "菜单及按钮", value = "根据id删除菜单或按钮!",type = "0") + @PostMapping("/deleteById") + @ApiOperation("根据id删除菜单或按钮") + @ResponseBody + public ResponseResult deleteById(@RequestParam String id) { + boolean ok = sysMenuService.deleteById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /********************************** + * 用途说明: 菜单或者按钮拖动 + * 参数说明 id + * 参数说明 id1 + * 返回值说明: com.yfd.platform.config.ResponseResult + ***********************************/ + @Log(module = "菜单及按钮", value = "拖动修改菜单或按钮同级顺序!") + @PostMapping("/changeMenuOrder") + @ApiOperation("菜单或按钮切换") + @ResponseBody + public ResponseResult changeMenuOrder(@RequestParam String fromId, + @RequestParam String toId) { + if (StrUtil.isBlank(fromId) || StrUtil.isBlank(toId)) { + return ResponseResult.error("参数为空!"); + } + if (fromId.equals(toId)) { + return ResponseResult.error("切换失败!"); + } + boolean ok = sysMenuService.changeOderNoById(fromId, toId); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + @PostMapping("/uploadIcon") + @ApiOperation("上传单个图标") + @ResponseBody + public ResponseResult uploadIcon(MultipartFile icon, String menuId) throws FileNotFoundException { + if (StrUtil.isNotBlank(menuId)) { + SysMenu sysMenu = sysMenuService.getById(menuId); + + //图片路径 + // String iconname = + // System.getProperty("user.dir") + "\\riis-system\\src\\main" + + // "\\resources\\static\\icon" + File.separator + sysMenu.getIcon(); + String iconname = systempath.replace("\\", "/") + "menuicon" + File.separator + sysMenu.getIcon(); + //删除图标 + new File(iconname).delete(); + } + String filename = sysMenuService.uploadIcon(icon); + SysMenu sysMenu = new SysMenu(); + sysMenu.setId(menuId); + sysMenu.setIcon(filename); + sysMenuService.updateById(sysMenu); + return ResponseResult.successData(filename); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysOrganizationController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysOrganizationController.java new file mode 100644 index 0000000..23364b2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysOrganizationController.java @@ -0,0 +1,279 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.mapper.SubstationMapper; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysOrganizationService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统组织框架 前端控制器 + *

+ * + * + * @since 2021-12-15 + */ +@RestController +@RequestMapping("/system/organization") +@Api(value = "SysOrganizationController", tags = "系统组织框架") +public class SysOrganizationController { + + @Resource + private ISysOrganizationService organizationService; + + @Resource + private SysRoleMapper sysRoleMapper; + + @Resource + private IUserService userService; + + @Resource + private SubstationMapper substationMapper; + + /*********************************** + * 用途说明:获取组织范围树结构 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + @PostMapping("/getOrgScopeTree") + @ApiOperation("获取组织范围树结构") + @ResponseBody + public List> getOrgScopeTree(String roleId) { + return organizationService.getOrgScopeTree(roleId); + } + + /*********************************** + * 用途说明:获取组织范围 + * 参数说明 + * 返回值说明: 组织范围集合 + ***********************************/ + @PostMapping("/getOrgTree") + @ApiOperation("获取组织结构树") + @ResponseBody + @PreAuthorize("@el.check('select:org')") + public List> getOrgTree(String parentid, + String params) { + return organizationService.getOrgTree(parentid, params); + } + + /*********************************** + * 用途说明:根据企业ID查询组织详情 + * 参数说明 + * id 企业id + * 返回值说明: 系统组织框架对象 + ***********************************/ + @PostMapping("/getOrganizationById") + @ApiOperation("根据企业ID查询组织信息") + @ResponseBody + public ResponseResult getOrganizationById(String id, String orgName) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("查询失败!"); + } + List> sysOrganizations = + organizationService.getOrganizationById(id, orgName); + // 查看变电站图片 + sysOrganizations.forEach(organization -> { + if (ObjectUtil.isNotEmpty(organization.get("orgcode"))) { + if (ObjectUtil.isNotEmpty(organization.get("contactPhone"))) { + String contactPhone = organization.get("contactPhone").toString(); + organization.put("contactPhone", DesensitizedUtil.mobilePhone(contactPhone)); + } + String orgcode = organization.get("orgcode").toString(); + List substations = + substationMapper.selectList(new LambdaQueryWrapper().eq(Substation::getStationCode, orgcode)); + if (substations.size() > 0) { + Substation substation = substations.get(0); + organization.put("images", substation.getImages()); + } + } + }); + return ResponseResult.successData(sysOrganizations); + } + + /*********************************** + * 用途说明:根据ID查询组织详情 + * 参数说明 + * id 系统组织id + * 返回值说明: 系统组织框架对象 + ***********************************/ + @PostMapping("/getOneById") + @ApiOperation("根据ID查询组织详情") + @ResponseBody + public ResponseResult getOneById(String id) { + SysOrganization sysOrganization = organizationService.getById(id); + String contactPhone = sysOrganization.getContactPhone(); + if (StrUtil.isNotBlank(contactPhone)) { + DesensitizedUtil.mobilePhone(contactPhone); + } + return ResponseResult.successData(sysOrganization); + } + + /*********************************** + * 用途说明:新增系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统组织框架", value = "新增企业或者部门!", type = "0") + @PostMapping("/addOrg") + @ApiOperation("新增系统组织框架") + @ResponseBody + public ResponseResult addOrg(@RequestBody SysOrganization sysOrganization) { + //判断是否是否填写 有效 否则默认为 1 + if (StrUtil.isEmpty(sysOrganization.getIsvaild())) { + sysOrganization.setIsvaild("1"); + } + String uuid = IdUtil.fastSimpleUUID(); + sysOrganization.setId(uuid); + String orgcode = sysOrganization.getOrgcode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysOrganization::getOrgcode, orgcode); + if (StrUtil.isNotBlank(sysOrganization.getParentid())) { + queryWrapper.eq(SysOrganization::getParentid, sysOrganization.getParentid()); + } else { + queryWrapper.eq(SysOrganization::getParentid, "0"); + } + List list = organizationService.list(queryWrapper); + if (list != null && list.size() > 0) { + return ResponseResult.error("当前区域或者变电站已经存在"); + } + String contactPhone = sysOrganization.getContactPhone(); + if (StrUtil.isNotBlank(contactPhone) && !Validator.isMobile(contactPhone)) { + return ResponseResult.error("手机号输入不合法"); + } + String custom1 = sysOrganization.getCustom1(); + if (StrUtil.isNotBlank(custom1)) { + String suffix = StrUtil.subAfter(custom1, ".", true).toLowerCase(); + if (!"png".equals(suffix) && !"jpg".equals(suffix) && !"gif".equals(suffix)) { + return ResponseResult.error("非法文件类型"); + } + } + //新增 系统组织R + boolean isOk = organizationService.addOrganization(sysOrganization); + /* SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + if (!("0").equals(sysOrganization.getParentid()) && usertype != 0) { + List roleByUserId = sysRoleMapper.getRoleByUserId(userInfo.getId()); + + }*/ + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否修改成功 + ***********************************/ + @Log(module = "系统组织框架", value = "修改企业或者部门信息!", type = "0") + @PostMapping("/updateById") + @ApiOperation("修改系统组织框架") + @ResponseBody + public ResponseResult updateById(@RequestBody SysOrganization sysOrganization) { + String orgcode = sysOrganization.getOrgcode(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysOrganization::getOrgcode, orgcode); + if (StrUtil.isNotBlank(sysOrganization.getParentid())) { + queryWrapper.eq(SysOrganization::getParentid, sysOrganization.getParentid()); + } else { + queryWrapper.eq(SysOrganization::getParentid, "0"); + } + queryWrapper.ne(SysOrganization::getId, sysOrganization.getId()); + List list = organizationService.list(queryWrapper); + if (list != null && list.size() > 0) { + return ResponseResult.error("当前区域或者变电站已经存在"); + } + String contactPhone = sysOrganization.getContactPhone(); + if (StrUtil.isNotBlank(contactPhone) && !Validator.isMobile(contactPhone)) { + return ResponseResult.error("手机号输入不合法"); + } + boolean isOk = organizationService.updateOrganization(sysOrganization); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否修改成功 + ***********************************/ + @Log(module = "系统组织框架", value = "设置企业/部门是否有效!", type = "0") + @PostMapping("/setIsValid") + @ApiOperation("设置组织是否有效") + @ResponseBody + public ResponseResult setIsValid(@RequestParam String id, + @RequestParam String isvaild) { + boolean isOk = organizationService.setIsValid(id, isvaild); + //boolean isOk = organizationService.update(updateWrapper); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除系统组织框架 + * 参数说明 + * id 系统组织框架id + * 返回值说明: 是否删除成功 + ***********************************/ + @Log(module = "系统组织框架", value = "根据ID删除企业或者部门!", type = "0") + @PostMapping("/deleteById") + @ApiOperation("根据id删除系统组织框架") + @ResponseBody + public ResponseResult deleteById(@RequestParam String id) { + /* String[] orgIds = id.split(","); + for (String orgId : orgIds) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + List list = + organizationService.list(queryWrapper.eq(SysOrganization::getParentid, orgId)); + List ids = + list.stream().map(SysOrganization::getId).collect(Collectors.toList()); + boolean isOk = organizationService.removeById(orgId); + if (!isOk) { + continue; + } + for (String oid : ids) { + organizationService.removeById(oid); + } + }*/ + boolean ok = organizationService.deleteById(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("删除失败"); + } + + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/SysRoleController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/SysRoleController.java new file mode 100644 index 0000000..11d2216 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/SysRoleController.java @@ -0,0 +1,413 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.modules.basedata.domain.SubstationPatroldevice; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.system.domain.RolePatroldevice; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.IRolePatroldeviceService; +import com.yfd.platform.system.service.ISysRoleService; +import com.yfd.platform.system.service.IUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 系统角色 前端控制器 + *

+ * + * + * @since 2021-12-15 + */ +@RestController +@RequestMapping("/system/role") +@Api(value = "SysRoleController", tags = "系统角色") +public class SysRoleController { + + @Resource + private ISysRoleService roleService; + + @Resource + private IUserService userService; + + @Resource + private IRolePatroldeviceService rolePatroldeviceService; + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + /*********************************** + * 用途说明:查询所有角色 + * 参数说明 + * roleName 角色名称 + * 返回值说明: 查询都有角色 + ***********************************/ + @PostMapping("/list") + @ApiOperation("查询所有角色") + @ResponseBody + @PreAuthorize("@el.check('select:role')") + public List list(String rolename) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotEmpty(rolename)) { + //根据角色名称模糊查询 + queryWrapper.like("rolename", rolename); + } + //根据角色级别,角色编号 正序排序 + queryWrapper.ne("level", "1").orderByAsc("level", "rolecode"); + return roleService.list(queryWrapper); + } + + /*********************************** + * 用途说明:根据Id获取当个角色 + * 参数说明 + * id 角色表id + * 返回值说明: 根据id查询到角色详情 + ***********************************/ + @PostMapping("/getOneById") + @ApiOperation("根据Id获取当个角色") + @ResponseBody + public ResponseResult getOneById(String id) { + SysRole sysRole = roleService.getById(id); + return ResponseResult.successData(sysRole); + } + + /*********************************** + * 用途说明:新增角色 + * 参数说明 + * sysRole 新增角色信息 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "新增角色",type = "0") + @PostMapping("/addRole") + @ApiOperation("新增角色") + @ResponseBody + @PreAuthorize("@el.check('add:role')") + public ResponseResult addRole(@RequestBody SysRole sysRole) { + String rolename = sysRole.getRolename(); + int count = roleService.count(new LambdaQueryWrapper().eq(SysRole::getRolename, rolename)); + if (count > 0) { + return ResponseResult.error("当前角色已经存在"); + } + boolean isOk = roleService.addRole(sysRole); + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:分配操作权限 + * 参数说明 + * id 角色id + * optscope 分配的权限 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "分配操作权限",type = "0") + @PostMapping("/setOptScope") + @ApiOperation("分配操作权限") + @ResponseBody + public ResponseResult setOptScope(@RequestParam String id, + @RequestParam String optscope) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新权限,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("optscope", optscope).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:角色分配摄像头 + * 参数说明 + * id 角色id + * deviceIds 设备id集合 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "角色分配摄像头",type = "0") + @PostMapping("/setPatrolDevice") + @ApiOperation("角色分配摄像头") + @ResponseBody + public ResponseResult setPatrolDevice(@RequestBody String param) { + JSONObject jsonObject = JSONUtil.parseObj(param); + String id = jsonObject.getStr("id"); + String deviceIds = jsonObject.getStr("deviceIds"); + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + roleService.setPatrolDevice(id, deviceIds); + return ResponseResult.success(); + } + + /*********************************** + * 用途说明:角色分配摄像头 + * 参数说明 + * id 角色id + * deviceIds 设备id集合 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "角色分配摄像头",type = "0") + @PostMapping("/getPatrolDeviceById") + @ApiOperation("查询角色分配的摄像头") + @ResponseBody + public ResponseResult getPatrolDeviceById(@RequestParam String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.success("参数为空"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(RolePatroldevice::getRoleid, id).select(RolePatroldevice::getDeviceid); + List list = rolePatroldeviceService.list(queryWrapper); + Map map = new HashMap<>(); + // 通过设备id获取变电站id + if (list.size() > 0) { + List collect = list.stream().map(RolePatroldevice::getDeviceid).collect(Collectors.toList()); + String deviceId = collect.get(0); + SubstationPatroldevice substationPatroldevice = substationPatroldeviceService.getById(deviceId); + String stationId = substationPatroldevice.getStationId(); + map.put("stationId", stationId); + map.put("id", collect); + } + return ResponseResult.successData(map); + } + + /*********************************** + * 用途说明:角色菜单权限 + * 参数说明 + * id 角色id + * menuIds 权限id字符串 + * 返回值说明: 是否分配成功 + ***********************************/ + @Log(module = "系统角色", value = "角色菜单权限",type = "0") + @PostMapping("/setMenuById") + @ApiOperation("角色菜单权限") + @ResponseBody + public ResponseResult setMenuById(String id, String menuIds) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + if (StrUtil.isBlank(menuIds)) { + return ResponseResult.success(); + } + boolean ok = roleService.setMenuById(id, menuIds); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + + } + + /*********************************** + * 用途说明:设置组织范围 + * 参数说明 + * id 角色id + * orgscope 组织范围 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "设置组织范围",type = "0") + @PostMapping("/setOrgscope") + @ApiOperation("设置组织范围") + @ResponseBody + public ResponseResult setOrgscope(@RequestParam String id, + @RequestParam String orgscope) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新组织范围,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("orgscope", orgscope).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:设置业务范围 + * 参数说明 + * id 角色id + * busscope 业务范围 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "设置业务范围",type = "0") + @PostMapping("/setBusscope") + @ApiOperation("设置业务范围") + @ResponseBody + public ResponseResult setBusscope(@RequestParam String id, + @RequestParam String busscope) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新业务范围,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("busscope", busscope).set( + "lastmodifier", userService.getUsername()).set( + "lastmodifydate", LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:角色添加用户 + * 参数说明 + * roleid 角色id + * userids 用户id组 + * 返回值说明: 是否新增成功 + ***********************************/ + @Log(module = "系统角色", value = "角色添加用户",type = "0") + @PostMapping("/setRoleUsers") + @ApiOperation("角色添加用户") + @ResponseBody + public ResponseResult setRoleUsers(String roleid, String userids) { + boolean isOk = true; + String[] temp = userids.split(","); + for (String userid : temp) { + isOk = isOk && userService.addUserRoles(roleid, userid); + } + if (isOk) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:删除角色用户 + * 参数说明 + * roleid 角色id + * 返回值说明: 是否新增成功 + ***********************************/ + @PostMapping("/deleteRoleUser") + @ApiOperation("删除角色用户") + @ResponseBody + public ResponseResult deleteRoleUsers(@RequestParam String roleid, + @RequestParam String userids) { + //根据角色id、用户id删除 + boolean ok = roleService.deleteRoleUsers(roleid, userids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:设置角色是否有效 + * 参数说明 + * id 角色id + *isvaild 是否有效(1 是 0 否 ) + * 返回值说明: 是否新增成功 + ***********************************/ + @PostMapping("/setIsvaild") + @ApiOperation("设置角色是否有效") + @ResponseBody + public ResponseResult setIsvaild(String id, String isvaild) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 更新业务范围,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("isvaild", isvaild).set("lastmodifier" + , userService.getUsername()).set("lastmodifydate", + LocalDateTime.now()); + boolean ok = roleService.update(updateWrapper); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:更新角色信息 + * 参数说明 + *sysRole 角色对象 + * 返回值说明: 是否修改成功 + ***********************************/ + @PostMapping("/updateById") + @ApiOperation("更新角色信息") + @ResponseBody + @PreAuthorize("@el.check('update:role')") + public ResponseResult updateById(@RequestBody SysRole sysRole) { + int count = + roleService.count(new LambdaQueryWrapper().ne(SysRole::getId, sysRole.getId()).eq(SysRole::getRolename, sysRole.getRolename())); + if (count > 0) { + return ResponseResult.error("当前角色已经存在"); + } + SysRole sysRole1 = roleService.getById(sysRole.getId()); + String level = sysRole1.getLevel(); + if ("4".equals(level)) { + return ResponseResult.error("安全审计员不可修改"); + } + //更新最近修改人 + sysRole.setLastmodifier(userService.getUsername()); + //更新最近修改时间 + sysRole.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //根据id更新角色信息 + boolean ok = roleService.updateById(sysRole); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除角色 + * 参数说明 + *id 角色id + * 返回值说明: 是否删除成功 + ***********************************/ + @PostMapping("/deleteById") + @ApiOperation("根据id删除角色") + @ResponseBody + @PreAuthorize("@el.check('del:role')") + public ResponseResult deleteById(@RequestParam String id) { + roleService.deleteById(id); + return ResponseResult.success(); + } + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + @PostMapping("/listRoleUsers") + @ApiOperation("查询已分配的用户") + @ResponseBody + public List listRoleUsers(String orgid, String username, + String status, String level, + String rolename, String isvaild) { + return roleService.listRoleUsers(orgid, username, status, level, + rolename, isvaild); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/controller/UserController.java b/riis-system/src/main/java/com/yfd/platform/system/controller/UserController.java new file mode 100644 index 0000000..5b8a111 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/controller/UserController.java @@ -0,0 +1,375 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.datasource.DataSource; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.RequestHolder; +import com.yfd.platform.utils.StringUtils; +import com.yfd.platform.utils.sm4.SM4Utils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 用户信息 前端控制器 + *

+ * + * + * @since 2022-09-20 + */ +@RestController +@RequestMapping("/system/user") +@Api(tags = "系统用户") +public class UserController { + + @Resource + private IUserService userService; + + @Resource + private ISysLogService sysLogService; + + @Value("${rsa.private_key}") + private String privateKey; + + @Log(module = "系统用户", value = "新增系统用户", type = "0") + @PostMapping("/addUser") + @ApiOperation("新增系统用户") + @ResponseBody + @PreAuthorize("@el.check('add:user')") + public ResponseResult addUser(@RequestBody SysUser user, String roleids) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysUser::getUsername, user.getUsername()); + int count = userService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前用户已经存在"); + } + String phone = user.getPhone(); + String email = user.getEmail(); + if (StrUtil.isNotBlank(email)) { + if (!Validator.isEmail(email)) { + return ResponseResult.error("邮箱输入不合法"); + } + } + if (StrUtil.isNotBlank(phone)) { + if (!Validator.isMobile(phone)) { + return ResponseResult.error("手机号输入不合法"); + } + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(SysUser::getPhone, user.getPhone()); + int count1 = userService.count(queryWrapper1); + if (count1 > 0) { + return ResponseResult.error("当前手机号已经绑定用户"); + } + } + Map reslut = userService.addUser(user, roleids); + return ResponseResult.successData(reslut); + } + + @Log(module = "系统用户", value = "修改用户信息", type = "0") + @PostMapping("/updateUser") + @ApiOperation("修改用户信息") + @ResponseBody + @PreAuthorize("@el.check('update:user')") + public ResponseResult updateUser(@RequestBody SysUser user, + String roleids) { + if (StrUtil.isEmpty(user.getId())) { + return ResponseResult.error("没有用户ID"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.ne(SysUser::getId, user.getId()).eq(SysUser::getUsername, user.getUsername()); + int count = userService.count(queryWrapper); + if (count > 0) { + return ResponseResult.error("当前用户已经存在"); + } + String phone = user.getPhone(); + String email = user.getEmail(); + if (StrUtil.isNotBlank(email)) { + if (!Validator.isEmail(email)) { + return ResponseResult.error("邮箱输入不合法"); + } + } + if (StrUtil.isNotBlank(phone)) { + if (!Validator.isMobile(phone)) { + return ResponseResult.error("手机号输入不合法"); + } + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(SysUser::getPhone, user.getPhone()).ne(SysUser::getId, user.getId()); + int count1 = userService.count(queryWrapper1); + if (count1 > 0) { + return ResponseResult.error("当前手机号已经绑定用户"); + } + } + //填写 当前用户名称 + user.setLastmodifier(userService.getUsername()); + //填写 当前日期 + user.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + Map reslut = userService.updateById(user, roleids); + return ResponseResult.successData(reslut); + } + + @Log(module = "系统用户", value = "解除用户锁定", type = "0") + @PostMapping("/unlockUser") + @ApiOperation("解除用户锁定") + @ResponseBody + public ResponseResult unlockUser(@RequestBody SysUser user) { + if (StrUtil.isEmpty(user.getId())) { + return ResponseResult.error("没有用户ID"); + } + //填写 当前用户名称 + user.setLastmodifier(userService.getUsername()); + //填写 当前日期 + user.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + boolean ok = userService.updateById(user); + if (!ok) { + return ResponseResult.error(); + } + return ResponseResult.success(); + } + + @GetMapping("/queryUserById") + @ApiOperation("根据ID查询用户信息") + @ResponseBody + public ResponseResult queryUserById(String id) { + SysUser sysUser = userService.getById(id); + return ResponseResult.successData(sysUser); + } + + @GetMapping("/queryUsers") + @ApiOperation("查询用户信息") + @ResponseBody + @PreAuthorize("@el.check('select:user')") + public ResponseResult queryUsers(String orgid, + String username, String status, Page page) { + + Page> mapPage = userService.queryUsers(orgid, + username, status, page); + return ResponseResult.successData(mapPage); + } + + @GetMapping("/queryUserByID") + @ApiOperation("查询用户保存的视频列表样式") + @ResponseBody + public ResponseResult queryUserByID(String id) { + SysUser sysUser = userService.getById(id); + return ResponseResult.successData(sysUser); + } + + /*********************************** + * 用途说明:用户分配角色 + * 参数说明 + *idMap 用户id与角色id + * 返回值说明: 判断是否添加成功 + ************************************/ + @Log(module = "系统用户", value = "用户分配角色及权限!", type = "0") + @PostMapping("/setUserRoles") + @ApiOperation("用户分配角色") + @ResponseBody + public ResponseResult setUserRoles(String roleid, String userids) { + boolean ok = userService.setUserRoles(roleid, userids); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:根据id删除用户 + * 参数说明 + *id 用户id + * 返回值说明: 判断是否删除成功 + ************************************/ + @Log(module = "系统用户", value = "根据ID删除用户", type = "0") + @PostMapping("/deleteById") + @ApiOperation("根据ID删除用户") + @ResponseBody + @PreAuthorize("@el.check('delete:user')") + public ResponseResult deleteById(String id) { + userService.deleteById(id); + return ResponseResult.success(); + } + + /*********************************** + * 用途说明:根据ID批量删除用户 + * 参数说明 + *ids 用户id集合 + * 返回值说明: 判断是否删除成功 + ************************************/ + @Log(module = "系统用户", value = "根据ID批量删除用户") + @PostMapping("/deleteUserByIds") + @ApiOperation("根据ID批量删除用户") + @ResponseBody + @PreAuthorize("@el.check('delete:user')") + public ResponseResult deleteUserByIds(String id) { + if (StrUtil.isBlank(id)) { + return ResponseResult.error("参数为空"); + } + boolean ok = userService.deleteUserByIds(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:重置用户密码(管理员) + * 参数说明 + *id 重置密码的 用户id + * 返回值说明: 判断是否重置成功 + ************************************/ + @Log(module = "系统用户", value = "重置用户密码") + @PostMapping("/resetPassword") + @ApiOperation("重置用户密码") + @ResponseBody + @DataSource + public ResponseResult resetPassword(String id) throws Exception { + if (StrUtil.isBlank(id)) { + ResponseResult.error("参数为空"); + } + boolean ok = userService.resetPassword(id); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error("重置用户密码失败!"); + } + } + + /*********************************** + * 用途说明:二次用户密码认证 + * 用户账号 username + *原密码 password + * 返回值说明: 判断是否认证成功! + ************************************/ + @PostMapping("/secondauthPassword") + @ApiOperation("二次用户密码认证") + @ResponseBody + @DataSource + public ResponseResult secondauthPassword(String username, String password) throws Exception { + String decryptpassword = SM4Utils.decrypt(password, privateKey); + boolean isok = userService.checkPassword(username, decryptpassword); + if (isok) { + return ResponseResult.success(); + } else { + return ResponseResult.successData("二次密码认证错误!"); + } + } + + /*********************************** + * 用途说明:用户更新密码(用户自己) + * 参数说明 + * 用户账号 username + *原密码 oldpassword + *新密码 newpassword + * 返回值说明: 判断是否重成功 + ************************************/ + @PostMapping("/updatePassword") + @ApiOperation("用户更新密码") + @ResponseBody + public ResponseResult updatePassword(@RequestBody String params) throws Exception { + JSONObject jsonObject = JSONUtil.parseObj(params); + String username = jsonObject.getStr("username"); + SysUser sysUser = userService.getUserInfo(username); + if (ObjUtil.isNull(sysUser)) { + return ResponseResult.error("您输入的用户名称不存在!"); + } + String decryptoldpassword = SM4Utils.decrypt(jsonObject.getStr("oldpassword"), privateKey); + String decryptnewpassword = SM4Utils.decrypt(jsonObject.getStr("newpassword"), privateKey); + + boolean isok = userService.checkPassword(username, decryptoldpassword); + if (isok == false) { + return ResponseResult.error("您输入的旧密码不正确,请重新输入!"); + } else { + userService.updatePassword(username, decryptnewpassword); + } + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + String requestip = StringUtils.getIp(request); + String brower = StringUtils.getBrowser(request); + SysLog sysLog = new SysLog(); + sysLog.setUsercode(username); + sysLog.setUsername(sysUser.getNickname()); + sysLog.setRequestip(requestip); + sysLog.setBrowser(brower); + sysLog.setType("0"); + sysLog.setOpttype("修改(update)"); + sysLog.setModule("系统用户"); + String className = this.getClass().getName(); + String method = + Thread.currentThread().getStackTrace()[1].getMethodName(); + sysLog.setMethod(className + "." + method + "()"); + sysLog.setDescription(sysUser.getNickname() + "用户更新密码!"); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); + return ResponseResult.success(String.format("您的密码更新成功,密码有限期为%s天!", sysUser.getPwdvalidperiod())); + } + + /*********************************** + * 用途说明:设置账号状态(管理员) + * 参数说明 + *id 用户id + * status 设置状态 + * 返回值说明: 判断是否设置成功 + ************************************/ + @Log(module = "系统用户", value = "设置账号状态,启用或者停止用户账号!", type = "0") + @PostMapping("/setStatus") + @ApiOperation("设置账号状态") + @ResponseBody + public ResponseResult setStatus(@RequestParam String id, + @RequestParam String status) { + boolean ok = userService.setStatus(id, status); + if (ok) { + return ResponseResult.success(); + } else { + return ResponseResult.error(); + } + } + + /*********************************** + * 用途说明:修改头像(管理员) + * 参数说明 + * multipartFile 文件对象 + * status 设置状态 + * 返回值说明: 文件名 + ************************************/ + @ApiOperation("修改头像") + @PostMapping(value = "/updateAvatar") + public ResponseResult updateAvatar(String id, MultipartFile multipartFile) { + if (multipartFile.isEmpty()) { + return ResponseResult.error("文件是空的"); + } +// String filename = multipartFile.getOriginalFilename(); +// String suffix = StrUtil.subAfter(filename, ".", true).toLowerCase(); +// if (!"png".equals(suffix) && !"jpg".equals(suffix) && !"gif".equals(suffix)) { +// return ResponseResult.error("非法文件类型"); +// } +// String contentType = multipartFile.getContentType(); +// if (!"image/jpeg".equals(contentType) && !"image/png".equals(contentType)&& !"image/gif".equals(contentType)) { +// return ResponseResult.error("非法文件内容"); +// } + boolean ok = userService.uploadAvatar(id, multipartFile); + return ResponseResult.success(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/Dictionary.java b/riis-system/src/main/java/com/yfd/platform/system/domain/Dictionary.java new file mode 100644 index 0000000..8fddadb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/Dictionary.java @@ -0,0 +1,78 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 数据字典表 + *

+ * + * + * @since 2021-10-27 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("rca_dictionary") +public class Dictionary implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 类型 + */ + private String type; + + /** + * 类型名称 + */ + private String typename; + + /** + * 代码 + */ + private String code; + + /** + * 名称 + */ + private String name; + + /** + * 顺序号 + */ + private String orderno; + + /** + * 上级代码 + */ + private String parentcode; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/LoginUser.java b/riis-system/src/main/java/com/yfd/platform/system/domain/LoginUser.java new file mode 100644 index 0000000..7e53f85 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/LoginUser.java @@ -0,0 +1,75 @@ +package com.yfd.platform.system.domain; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LoginUser implements UserDetails { + + private SysUser user; + + private List permissions; + + public LoginUser(SysUser user, List permissions) { + this.user = user; + this.permissions = permissions; + } + + @JSONField(serialize = false) + private List authorities; + + @Override + public Collection getAuthorities() { + // 将权限信息放入集合 + authorities = permissions.stream() + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + return authorities; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + //获取用户昵称 + public String geNickname() { + return user.getNickname(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/Message.java b/riis-system/src/main/java/com/yfd/platform/system/domain/Message.java new file mode 100644 index 0000000..745a789 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/Message.java @@ -0,0 +1,115 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 消息通知 + *

+ * + * + * @since 2023-03-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_message") +public class Message implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(type = IdType.ASSIGN_UUID) + @ApiModelProperty(value = "ID") + private String id; + + /** + * 创建时间:排序 + */ + @ApiModelProperty(value = "创建时间:排序") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp createtime; + + /** + * 消息类型:1-定时任务 2-工作流触发 3-人工触发 + */ + @ApiModelProperty(value = "消息类型:1-定时任务 2-工作流触发 3-人工触发") + private String type; + + /** + * 消息标题 + */ + @ApiModelProperty(value = "消息标题") + private String title; + + /** + * 消息内容 + */ + @ApiModelProperty(value = "消息内容") + private String content; + + /** + * 发送者名称,定时器,人员 + */ + @ApiModelProperty(value = "发送者名称,定时器,人员") + private String senderName; + + /** + * 接收者代码 人员账号列表 + */ + @ApiModelProperty(value = "接收者代码 人员账号列表 ") + private String receiverCodes; + + /** + * 接收者名称:为空 即为所有人,人员名称列表 + */ + @ApiModelProperty(value = "接收者名称:为空 即为所有人,人员名称列表") + private String receiverNames; + + /** + * 状态:1、初始创建 2-消息已阅 9-消息过期 + */ + @ApiModelProperty(value = "状态:1、初始创建 2-消息已阅 9-消息过期") + private String status; + + /** + * 有效期:小时 + */ + @ApiModelProperty(value = "有效期:小时") + private Integer validperiod; + + /** + * 已阅时间 + */ + @ApiModelProperty(value = "已阅时间") + private Timestamp readtime; + + /** + * 备用1 + */ + @ApiModelProperty(value = "备用1") + private String custom1; + + /** + * 备用2 + */ + @ApiModelProperty(value = "备用2") + private String custom2; + + /** + * 备用3 + */ + @ApiModelProperty(value = "备用3") + private String custom3; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/QuartzJob.java b/riis-system/src/main/java/com/yfd/platform/system/domain/QuartzJob.java new file mode 100644 index 0000000..c2cc1db --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/QuartzJob.java @@ -0,0 +1,116 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 定时任务 + *

+ * + * + * @since 2023-03-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_quartz_job") +public class QuartzJob implements Serializable { + + public static final String JOB_KEY = "JOB_KEY"; + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @ApiModelProperty(value = "ID") + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 排序号 + */ + @ApiModelProperty(value = "排序号") + private Integer orderno; + + /** + * 任务名称 + */ + @ApiModelProperty(value = "任务名称") + private String jobName; + + /** + * 执行类名称 + */ + @ApiModelProperty(value = "执行类名称") + private String jobClass; + + /** + * 执行方法名称 + */ + @ApiModelProperty(value = "执行方法名称") + private String jobMethod; + + /** + * 时间周期表达式 + */ + @ApiModelProperty(value = "时间周期表达式") + private String jobCron; + + /** + * 方法参数 + */ + @ApiModelProperty(value = "方法参数") + private String jobParams; + + /** + * 任务描述 + */ + @ApiModelProperty(value = "任务描述") + private String description; + + /** + * 状态:0-暂停、1-启用 + */ + @ApiModelProperty(value = "状态:0-暂停、1-启用") + private String status; + + /** + * 最近修改者 + */ + @ApiModelProperty(value = "最近修改者") + private String lastmodifier; + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @ApiModelProperty(value = "最近修改日期") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + @ApiModelProperty(value = "备用1") + private String custom1; + + /** + * 备用2 + */ + @ApiModelProperty(value = "备用2") + private String custom2; + + /** + * 备用3 + */ + @ApiModelProperty(value = "备用3") + private String custom3; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/RolePatroldevice.java b/riis-system/src/main/java/com/yfd/platform/system/domain/RolePatroldevice.java new file mode 100644 index 0000000..de29513 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/RolePatroldevice.java @@ -0,0 +1,43 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 系统角色摄像头对照 + *

+ * + * + * @since 2023-04-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_role_patroldevice") +public class RolePatroldevice implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 角色id + */ + private String roleid; + + /** + * 摄像头配置 + */ + private String deviceid; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysConfig.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysConfig.java new file mode 100644 index 0000000..5630cb3 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysConfig.java @@ -0,0 +1,81 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统全局配置 + *

+ * + * + * @since 2022-01-19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_config") +public class SysConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 系统编号 + */ + private String syscode; + + + /** + * 系统名称 + */ + private String sysname; + + /** + * 系统功能介绍 + */ + private String sysdesc; + + /** + * 系统版本信息 + */ + private String version; + + /** + * 备注 + */ + private String remark; + + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysDataBackup.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysDataBackup.java new file mode 100644 index 0000000..7109c56 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysDataBackup.java @@ -0,0 +1,85 @@ +package com.yfd.platform.system.domain; + +import java.time.LocalDateTime; +import java.io.Serializable; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 系统数据备份恢复记录 + *

+ * + * + * @since 2023-08-31 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysDataBackup implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 数据范围 全部数据 基础数据 业务数据 + */ + private String datascope; + + /** + * 备份文件名称 + */ + private String filename; + + /** + * 类型 自动备份 手工备份 + */ + private String backuptype; + + /** + * 备份日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime backupdate; + + /** + * 单位:Kb + */ + private String filesize; + + /** + * 恢复日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime recoverdate; + + /** + * 恢复操作人员 + */ + private String recover; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysDictionary.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysDictionary.java new file mode 100644 index 0000000..0d6fcea --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysDictionary.java @@ -0,0 +1,71 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 数据字典表 + *

+ * + * + * @since 2023-03-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysDictionary implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 字典类型 00-系统内置 01-用户配置 + */ + @TableField("dicttype") + private String dictType; + + /** + * 顺序号 + */ + @TableField("orderno") + private Integer orderNo; + + /** + * 字典编码 + */ + @TableField("dictcode") + private String dictCode; + + /** + * 字典名称 + */ + @TableField("dictname") + private String dictName; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysDictionaryItems.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysDictionaryItems.java new file mode 100644 index 0000000..62074c4 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysDictionaryItems.java @@ -0,0 +1,77 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + *

+ * 数据字典明细 + *

+ * + * + * @since 2023-03-08 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class SysDictionaryItems implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 对应字典ID + */ + @TableField("dictid") + private String dictId; + + /** + * 顺序号 + */ + @TableField("orderno") + private Integer orderNo; + + /** + * 项编码 + */ + @TableField("itemcode") + private String itemCode; + + /** + * 项名称 + */ + @TableField("dictname") + private String dictName; + + /** + * 父项编码 + */ + @TableField("parentcode") + private String parentCode; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysLog.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysLog.java new file mode 100644 index 0000000..7fd002a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysLog.java @@ -0,0 +1,98 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统操作日志 + *

+ * + * + * @since 2023-03-08 + */ +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class SysLog implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户账号 + */ + @TableField("usercode") + private String usercode; + + /** + * 用户名称 + */ + private String username; + + /** + * 操作类型 00-登录 01-新增 02-修改 03-删除 06-查询 09其他 + */ + @TableField("opttype") + private String opttype; + + /** + * 模块名称 + */ + private String module; + + /** + * 日志描述 + */ + private String description; + + /** + * 操作方法 + */ + private String method; + + /** + * 方法参数 + */ + private String params; + + /** + * 创建时间 + */ + @TableField("logtime") + private Timestamp logtime; + + /** + * 请求IP + */ + @TableField("requestip") + private String requestip; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 0:系统日志 ,1:业务日志 + */ + private String type; + + + + public SysLog(String opttype) { + this.opttype = opttype; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysMenu.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysMenu.java new file mode 100644 index 0000000..d63312e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysMenu.java @@ -0,0 +1,113 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 菜单及按钮 + *

+ * + * + * @since 2021-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_menu") +public class SysMenu implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 1-web 2-pad 3-mobile + */ + private String systemcode; + + /** + * 1-菜单 2-按钮 + */ + private String type; + + /** + * 在系统内自动生成 + */ + private String code; + + /** + * 名称 + */ + private String name; + + /** + * 图标地址 + */ + private String icon; + + /** + * 是否外链 + */ + private String islink; + + /** + * 内部模块路径或者外链地址 + */ + private String opturl; + + /** + * 权限控制标识 + */ + private String permission; + + /** + * 顶级为0 + */ + private String parentid; + + /** + * 排序号 + */ + private Integer orderno; + + /** + * 0-不显示 1-显示 + */ + private String isdisplay; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysOrganization.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysOrganization.java new file mode 100644 index 0000000..03033c7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysOrganization.java @@ -0,0 +1,153 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统组织框架 + *

+ * + * + * @since 2021-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_organization") +public class SysOrganization implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 1-公司 -2-部门 + */ + private String orgtype; + + /** + * 两位一级 + */ + private String orgcode; + + /** + * 组织名称 + */ + private String orgname; + + /** + * 上级id + */ + private String parentid; + + /** + * 组织负责人 + */ + private String manager; + + /** + * 1-是 0-否 + */ + private String isvaild; + + /** + * 描述 + */ + private String description; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + /** + * 区域节点IP + */ + private String nodeIp; + + /** + * 区域节点ID + */ + private String nodeId; + + /** + * 所属地市 + */ + private String cityName; + + /** + * 所属省份 + */ + private String provinceName; + + /** + * 公司名称 + */ + private String companyName; + + /** + * 区域地址 + */ + private String address; + + /** + * 联系电话 + */ + private String contactPhone; + + /** + * 联系人 + */ + private String contactPerson; + + /** + * 电压等级 + */ + private String voltLevel; + + /** + * 变电站类别 + */ + private String stationType; + + /** + * 是否是边缘节点 0:否,1:是 + */ + private String isStationFlag; + + /** + * 经纬度 + */ + private String coordinate; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysRole.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysRole.java new file mode 100644 index 0000000..3ffbf27 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysRole.java @@ -0,0 +1,99 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统角色 + *

+ * + * + * @since 2021-12-15 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_role") +public class SysRole implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 系统生成,三位编号 + */ + private String rolecode; + + /** + * 角色名称 + */ + private String rolename; + + /** + * 1-超级管理员 2-单位管理员 3-普通用户 + */ + private String level; + + /** + * 描述 + */ + private String description; + + /** + * org1,org2 + */ + private String orgscope; + + /** + * 多个操作代码(菜单、按钮) + */ + private String optscope; + + /** + * json格式自定义业务范围 + */ + private String busscope; + + /** + * 1-是 0-否 + */ + private String isvaild; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/domain/SysUser.java b/riis-system/src/main/java/com/yfd/platform/system/domain/SysUser.java new file mode 100644 index 0000000..57644b8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/domain/SysUser.java @@ -0,0 +1,138 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.sql.Timestamp; + +/** + *

+ * 系统用户 + *

+ * + * + * @since 2021-10-27 + */ + +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("sys_user") +public class SysUser implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * id 主键 + */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** + * 用户类型 0-管理员 1-普通用户 + */ + private Integer usertype; + + /** + * 用户名(账号) + */ + private String username; + /** + * 用户昵称 + */ + private String nickname; + + /** + * 登录密码(加密存储) + */ + private String password; + + /** + * 密码有效期限 + */ + private Integer pwdvalidperiod; + + /** + * 登录失败次数 + */ + private Integer failednum; + + /** + * 登录失败三次锁定时间 + */ + private String failedlocktime; + + /** + * 性别(0-男 1-女 ) + */ + private String sex; + + /** + * 邮箱 + */ + private String email; + /** + * 手机号 + */ + private String phone; + + /** + * 头像(预留) + */ + private String avatar; + + /** + * 账号状态(1-正常 0-停用 2-失败锁定) + */ + private Integer status; + + /** + * 部门ID + */ + private String orgid; + + /** + * 密码重置时间 + */ + private String pwdresettime; + + /** + * 最近修改者 + */ + private String lastmodifier; + + /** + * 最近修改日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp lastmodifydate; + + @TableField(exist = false) + private String uuid; + + @TableField(exist = false) + private String code; + + + /** + * 允许的登录IP + */ + private String loginip; + + /** + * 备用1 + */ + private String custom1; + + /** + * 备用2 + */ + private String custom2; + + /** + * 备用3 + */ + private String custom3; +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/MessageMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/MessageMapper.java new file mode 100644 index 0000000..1251f24 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/MessageMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.Message; + +/** + *

+ * 消息通知 Mapper 接口 + *

+ * + * + * @since 2023-03-19 + */ +public interface MessageMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/QuartzJobMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/QuartzJobMapper.java new file mode 100644 index 0000000..b54e4ee --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/QuartzJobMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.QuartzJob; + +/** + *

+ * 定时任务 Mapper 接口 + *

+ * + * + * @since 2023-03-19 + */ +public interface QuartzJobMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/RolePatroldeviceMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/RolePatroldeviceMapper.java new file mode 100644 index 0000000..e2a74ee --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/RolePatroldeviceMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.RolePatroldevice; + +/** + *

+ * 系统角色摄像头对照 Mapper 接口 + *

+ * + * + * @since 2023-04-23 + */ +public interface RolePatroldeviceMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysConfigMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..3954f9b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysConfigMapper.java @@ -0,0 +1,17 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysConfig; + + +/** + *

+ * 系统全局配置 Mapper 接口 + *

+ * + * + * @since 2022-01-19 + */ +public interface SysConfigMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDataBackupMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDataBackupMapper.java new file mode 100644 index 0000000..01d47f8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDataBackupMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.yfd.platform.system.domain.SysDataBackup; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 系统数据备份恢复记录 Mapper 接口 + *

+ * + * + * @since 2023-08-31 + */ +public interface SysDataBackupMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDictionaryItemsMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDictionaryItemsMapper.java new file mode 100644 index 0000000..6374974 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDictionaryItemsMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysDictionaryItems; + +/** + *

+ * 数据字典明细 Mapper 接口 + *

+ * + * + * @since 2023-03-08 + */ +public interface SysDictionaryItemsMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDictionaryMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDictionaryMapper.java new file mode 100644 index 0000000..0edcf5c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysDictionaryMapper.java @@ -0,0 +1,22 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysDictionary; + +/** + *

+ * 数据字典表 Mapper 接口 + *

+ * + * + * @since 2023-03-08 + */ +public interface SysDictionaryMapper extends BaseMapper { + + /********************************** + * 用途说明: 根据字典类型获取字典最大序号 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: 返回增加成功或者失败 + ***********************************/ + Integer selectMaxNo(String dictType); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysLogMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysLogMapper.java new file mode 100644 index 0000000..f828704 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysLogMapper.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysLog; + +/** + *

+ * 系统操作日志 Mapper 接口 + *

+ * + * + * @since 2023-03-08 + */ +public interface SysLogMapper extends BaseMapper { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysMenuMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..6b1b154 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysMenuMapper.java @@ -0,0 +1,67 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 菜单及按钮 Mapper 接口 + *

+ * + * + * @since 2021-12-15 + */ +public interface SysMenuMapper extends BaseMapper { + + /*********************************** + * 用途说明:菜单及按钮序号向上移动 + * 参数说明 + * parentid 上级id + *Orderno 小于序号(原序号) + *upOrderno 大于等于序号(更改的序号加一) + * 返回值说明: 是否更新成功 + ***********************************/ + boolean upMoveOrderno(@Param("parentid") String parentid, @Param("Orderno") int Orderno, @Param("upOrderno") int upOrderno); + + /*********************************** + * 用途说明:菜单及按钮序号向下移动 + * 参数说明 + * parentid 上级id + *Orderno 大于序号(原序号) + *downOrderno 小于等于序号(更改的序号减一) + * 返回值说明: 是否更新成功 + ***********************************/ + boolean downMoveOrderno(@Param("parentid") String parentid, @Param("Orderno") int Orderno, @Param("downOrderno") int downOrderno); + + + List selectPermsByUserId(String userId); + + //List selectMenuByUserId(String userId); + List> selectMenuByUserId(String userId); + + /*********************************** + * 用途说明:根据权限id查找系统类型 + * 参数说明 id 权限id + * 返回值说明: 返回系统类型 + ***********************************/ + String getSystemCodeById(String id); + + /*********************************** + * 用途说明:根据角色Id查找权限 + * 参数说明 id 权限id + * 返回值说明: 返回权限集合 + ***********************************/ + List selectMenuByRoleId(String id); + + /********************************** + * 用途说明: 根据系统类型获取用户菜单 + * 参数说明 id + * 参数说明 sysetmCode + * 返回值说明: java.util.List> + ***********************************/ + List> selectMenuByType(String id, String systemCode); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysOrganizationMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysOrganizationMapper.java new file mode 100644 index 0000000..ad43f90 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysOrganizationMapper.java @@ -0,0 +1,33 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysOrganization; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 系统组织框架 Mapper 接口 + *

+ * + * + * @since 2021-12-15 + */ +public interface SysOrganizationMapper extends BaseMapper { + + /*********************************** + * 用途说明:去重查询组织分类 + * 返回值说明: 所有组织分类 + ***********************************/ + List queryOrgtype(); + + /*********************************** + * 用途说明:根据组织分类查询上级id + * 参数说明 + * orgtype 组织分类 + * 返回值说明: 上级id + ***********************************/ + List queryParentid(@Param("orgtype") String orgtype); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysRoleMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..d87d75c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysRoleMapper.java @@ -0,0 +1,89 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统角色 Mapper 接口 + *

+ * + * + * @since 2021-12-15 + */ +public interface SysRoleMapper extends BaseMapper { + + /*********************************** + * 用途说明:根据角色id查询是否存在用户 + * 参数说明 + * roleid 角色id + * 返回值说明: 该角色下是否存在用户 + ************************************/ + List> isRoleUsersByroleid(String roleid); + + /*********************************** + * 用途说明:根据角色id查询是否存在权限 + * 参数说明 + * roleid 角色id + * 返回值说明: 该角色下是否存在权限 + ************************************/ + List> isRoleMenuByRoleId(String roleId); + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 '1-超级管理员 2-单位管理员 3-普通用户' + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + List listRoleUsers(String orgid, String username, String status, + String level, String rolename, String isvaild); + + /*********************************** + * 用途说明:根据 角色id和用户id 删除 (admin除外) + * 参数说明 + *roleid 角色id + * urserid 用户id + * 返回值说明: 是否删除成功 + ***********************************/ + boolean deleteRoleUsers(String roleid, String urserid); + + /********************************** + * 用途说明: 根据用户id获取角色信息 + * 参数说明 id 角色id + * 返回值说明: void + ***********************************/ + List getRoleByUserId(String id); + + /********************************** + * 用途说明: 根据角色ID删除菜单与角色关联信息 + * 参数说明 id 角色id + * 返回值说明: void + ***********************************/ + boolean deleteRoleMenus(String id); + + /********************************** + * 用途说明: 根据角色ID删除用户与角色关联信息 + * 参数说明 id 角色id + * 返回值说明: void + ***********************************/ + boolean deleteRoleUser(String id); + + /********************************** + * 用途说明: 根据角色id获取用户id + * 参数说明 id 角色id + * 返回值说明: 用户id + ***********************************/ + List getUserIdById(String id); + + void addRoleMenu(@Param("id") String id, @Param("roleid") String roleid, + @Param("menuid") String menuid); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..91df6d6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java @@ -0,0 +1,103 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.system.domain.SysUser; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统用户表 Mapper 接口 + *

+ * + * + * @since 2021-10-27 + */ +public interface SysUserMapper extends BaseMapper { + List list(@Param("total")String total, @Param("size")String size, @Param("orgid")String orgid, @Param("username")String username, @Param("mobile")String mobile , @Param("status")String status); + + /*********************************** + * 用途说明:新增系统角色用户对照表 对用户分配角色 + * 参数说明 + * id 生成的id + * roleid 角色id + * userid 用户id + * 返回值说明: + ************************************/ + boolean addUserRoles(@Param("id")String id,@Param("roleid") String roleid,@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户id 和角色id 查询 系统角色用户对照表 + * 参数说明 + * userid 用户id + * roleid 角色id + * 返回值说明: + ************************************/ + List getRoleUsersByid(@Param("roleid") String roleid,@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户表id查询角色表所有角色 + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + List getLevel(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户表id查询角色表所有角色id + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + List getRoleid(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户表id查询角色表级别 + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + String getMaxLevel(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户id删除所分配的角色 + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + boolean delRoleUsersByUserid(@Param("userid") String userid); + + /*********************************** + * 用途说明:根据用户id删除所分配的不包含角色 + * 参数说明 + * userid 用户id + * roleids 多个角色id + * 返回值说明: + ************************************/ + boolean delInRoleUsersByUserid(@Param("userid") String userid,@Param("roleids")String[] roleids); + + Page> queryUsers(String orgid, + String username, + String status, + Page page); + + Map getOrganizationByid(String id); + + /********************************** + * 用途说明: 根据ID删除用户与角色的关联信息 + * 参数说明 ids 用户id集合 + * 返回值说明: void + ***********************************/ + void delRoleUsersByUserIds(List ids); + + /*********************************** + * 用途说明: 获取审计管理员用户 + * 参数说明 + * 返回值说明: java.util.List + ***********************************/ + List getAuditManagement(); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/IMessageService.java b/riis-system/src/main/java/com/yfd/platform/system/service/IMessageService.java new file mode 100644 index 0000000..c8bc860 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/IMessageService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.Message; + +/** + *

+ * 消息通知 服务类 + *

+ * + * + * @since 2023-03-19 + */ +public interface IMessageService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/IQuartzJobService.java b/riis-system/src/main/java/com/yfd/platform/system/service/IQuartzJobService.java new file mode 100644 index 0000000..bbbe772 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/IQuartzJobService.java @@ -0,0 +1,43 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.QuartzJob; + +/** + *

+ * 定时任务 服务类 + *

+ * + * + * @since 2023-03-19 + */ +public interface IQuartzJobService extends IService { + + /********************************** + * 用途说明: 新增定时任务 + * 参数说明 quartzJob 定时对象 + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean addQuartzJob(QuartzJob quartzJob); + + /********************************** + * 用途说明: 删除定时任务 + * 参数说明 id id + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean deleteQuartzJob(String id); + + /********************************** + * 用途说明: 拖动修改定时任务顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + boolean changeDictOrder(String fromID, String toID); + + /********************************** + * 用途说明: 执行定时任务 + * 参数说明 id id + * 返回值说明: void + ***********************************/ + void execution(QuartzJob byId); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/IRolePatroldeviceService.java b/riis-system/src/main/java/com/yfd/platform/system/service/IRolePatroldeviceService.java new file mode 100644 index 0000000..2c41012 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/IRolePatroldeviceService.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.RolePatroldevice; + +/** + *

+ * 系统角色摄像头对照 服务类 + *

+ * + * + * @since 2023-04-23 + */ +public interface IRolePatroldeviceService extends IService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysConfigService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysConfigService.java new file mode 100644 index 0000000..1a1fdb1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysConfigService.java @@ -0,0 +1,42 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysConfig; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; + +/** + *

+ * 系统全局配置 服务类 + *

+ * + * + * @since 2022-01-19 + */ +public interface ISysConfigService extends IService { + + /********************************** + * 用途说明: 上传系统操作手册 + * 参数说明 file 文件域 + * 返回值说明: void + ***********************************/ + void uploadModelFile(MultipartFile file); + + /********************************** + * 用途说明: 下载操作手册 + * 参数说明 fileName 文件名称 + * 参数说明 response + * 返回值说明: void + ***********************************/ + void downloadConfigFile(String fileName, HttpServletResponse response); + + /********************************** + * 用途说明: 删除文件 + * 参数说明 id 配置id + * 参数说明 fileName 文件名称 + * 返回值说明: boolean + ***********************************/ + boolean deleteConfigFile(String id, String fileName); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysDataBackupService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysDataBackupService.java new file mode 100644 index 0000000..10e133f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysDataBackupService.java @@ -0,0 +1,53 @@ +package com.yfd.platform.system.service; + +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.domain.SysDataBackup; +import com.baomidou.mybatisplus.extension.service.IService; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + *

+ * 系统数据备份恢复记录 服务类 + *

+ * + * + * @since 2023-08-31 + */ +public interface ISysDataBackupService extends IService { + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 page分页对象、datascope(数据范围)、backuptype(backuptype) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明:返回分页查询结果 + ***********************************/ + Page getBackFileList(String filename, String datascope, String backuptype, String startDate, + String endDate, Page page); + + /********************************** + * 用途说明: 备份数据库表 + * 参数说明 conninfo 数据库连接信息 + * 参数说明 datascope 数据范围:全部数据 基础数据 业务数据 + * 参数说明 backuptype 类型 自动备份 手工备份 + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean backupDataTable(String datascope, String backuptype); + + /********************************** + * 用途说明: 恢复数据库表 + * 参数说明 id 数据库表备份记录id + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean recoverDataTable(String id); + + /********************************** + * 用途说明: 下载数据库文件 + * 参数说明 id 数据表备份文件记录id + * 返回值说明: boolean 是否成功 + ***********************************/ + boolean downloadDataTable(String id, HttpServletResponse response); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysDictionaryItemsService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysDictionaryItemsService.java new file mode 100644 index 0000000..10dc53e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysDictionaryItemsService.java @@ -0,0 +1,55 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysDictionaryItems; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + *

+ * 数据字典明细 服务类 + *

+ * + * + * @since 2023-03-08 + */ +public interface ISysDictionaryItemsService extends IService { + + /********************************** + * 用途说明: 分页查询字典项信息 + * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + Page getDictItemPage(String dictId, String itemName, Page page); + + /********************************** + * 用途说明: 增加字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + boolean addDictionaryItem(SysDictionaryItems sysDictionaryItems); + + /********************************** + * 用途说明: 拖动修改字典项顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + boolean changeItemOrder(String fromID, String toID); + + /********************************** + * 用途说明: 导出数据字典项数据 + * 参数说明 sysDictionaryItemsList 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或失败 + ***********************************/ + void exportExcel(List records, HttpServletResponse response); + + /********************************** + * 用途说明: 获取类型 + * 参数说明 dictcode + * 返回值说明: java.util.List> + ***********************************/ + List> getDeviceByType(String dictcode); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysDictionaryService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysDictionaryService.java new file mode 100644 index 0000000..f497790 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysDictionaryService.java @@ -0,0 +1,45 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysDictionary; + +import java.util.List; + +/** + *

+ * 数据字典表 服务类 + *

+ * + * + * @since 2023-03-08 + */ +public interface ISysDictionaryService extends IService { + + /********************************** + * 用途说明: 获取数据字典列表 + * 参数说明 dictType 字典类型 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + List getDictList(String dictType); + + /********************************** + * 用途说明: 新增字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + boolean addDict(SysDictionary sysDictionary); + + /********************************** + * 用途说明: 根据ID删除字典 + * 参数说明 id 字典ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除结果成功或者失败 + ***********************************/ + boolean deleteDictById(String id); + + /********************************** + * 用途说明: 拖动修改字典顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + boolean changeDictOrder(String fromID, String toID); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysLogService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysLogService.java new file mode 100644 index 0000000..4cf2bc1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysLogService.java @@ -0,0 +1,57 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysLog; +import org.aspectj.lang.ProceedingJoinPoint; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + *

+ * 系统操作日志 服务类 + *

+ * + * + * @since 2023-03-08 + */ +public interface ISysLogService extends IService { + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 pageNum(页码数)、pageSize(页大小,如果是固定页大小可不传)、username(用户名)、(optType) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + Page getLogList(String username, String optType, String requestip, String module,String type, String startDate, + String endDate, String orderfield, Page page); + + + /********************************** + * 用途说明: 导出日志数据 + * 参数说明 sysLogs 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + void exportExcel(String username, String optType,String requestip,String module, + String type,String startDate, + String endDate, String orderfield, HttpServletResponse response) throws IOException; + + /********************************** + * 用途说明: 新增日志 + * 参数说明 nickname 用户名 + * 参数说明 username 用户账号 + * 参数说明 browser 浏览器 + * 参数说明 ip 本机Ip地址 + * 参数说明 joinPoint 连接点 + * 参数说明 log 日志信息 + * 返回值说明: void + ***********************************/ + void save(String nickname,String username, String browser, String ip, ProceedingJoinPoint joinPoint, SysLog log); + /********************************** + * 用途说明: 删除6个月前的数据 + * 参数说明 s无 + * 返回值说明: 无 + ***********************************/ + void deleteLogs(); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysMenuService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysMenuService.java new file mode 100644 index 0000000..5816af1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysMenuService.java @@ -0,0 +1,108 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysMenu; +import org.springframework.web.multipart.MultipartFile; + +import java.io.FileNotFoundException; +import java.util.List; +import java.util.Map; + +/** + *

+ * 菜单及按钮 服务类 + *

+ * + * + * @since 2021-12-15 + */ +public interface ISysMenuService extends IService { + + /*********************************** + * 用途说明:获取菜单结构树(含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + List> getMenuButtonTree(String systemcode, String name, String isdisplay); + + /*********************************** + * 用途说明:获取菜单结构树(不含按钮) + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + List> getMenuTree(String systemcode, String name, String isdisplay); + + + /*********************************** + * 用途说明:新增菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否添加成功提示 + ***********************************/ + boolean addMenu(SysMenu sysMenu); + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * id 上传图标id + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + boolean uploadIcon(String id, MultipartFile icon); + + /*********************************** + * 用途说明:根据id删除单个图标 + * 参数说明 + * id 删除图标id + * icon 图标名称 + * 返回值说明: 是否删除成功 + ***********************************/ + boolean deleteIcon(String id); + + /*********************************** + * 用途说明:菜单及按钮序号排序 + * 参数说明 + * parentid 上级id + * orderMap map<菜单及按钮表id,排列序号> + * 返回值说明: 是否更新成功 + ***********************************/ + boolean moveOrderno(String parentid, String id, int orderno); + + /*********************************** + * 用途说明:根据id删除菜单或按钮 + * 参数说明 + * id 删除列的id + * 返回值说明: 是否删除成功 + ***********************************/ + boolean deleteById(String id); + + boolean changeOderNoById(String fromId, String toId); + + List> getMenuTree(String id); + + /*********************************** + * 用途说明:权限分配 + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + List> permissionAssignment(String code,String roleId); + + String uploadIcon(MultipartFile icon) throws FileNotFoundException; + + /********************************** + * 用途说明: 根据系统类型获取用户菜单结构树 + * 参数说明 id + * 参数说明 sysetmCode + * 返回值说明: java.util.List> + ***********************************/ + List> getMenuTreeByType(String id, String sysetmCode); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysOrganizationService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysOrganizationService.java new file mode 100644 index 0000000..f8816a6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysOrganizationService.java @@ -0,0 +1,97 @@ +package com.yfd.platform.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysOrganization; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * 系统组织框架 服务类 + *

+ * + * + * @since 2021-12-15 + */ +public interface ISysOrganizationService extends IService { + + /*********************************** + * 用途说明:获取组织结构树 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + List> getOrgTree(String parentid, String params); + + /*********************************** + * 用途说明:新增系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否新增成功 + ***********************************/ + boolean addOrg(SysOrganization sysOrganization); + + /*********************************** + * 用途说明:根据企业ID查询组织详情 + * 参数说明 + * id 企业id + * 返回值说明: 系统组织框架对象 + ***********************************/ + List> getOrganizationById(String id,String orgName); + + /*********************************** + * 用途说明:获取组织范围树结构 + * 参数说明 + *roleId 角色id + * 返回值说明: 组织树集合 + ***********************************/ + List> getOrgScopeTree(String roleId); + + /********************************** + * 用途说明: 修改角色组织范围 + * 参数说明 roleId 角色id + * 参数说明 orgscope 组织id集合 + * 返回值说明: boolean 是否修改成功 + ***********************************/ + boolean updateOrgScopeByRoleId(String roleId, String orgscope); + + /********************************** + * 用途说明: 获取用户所对应的组织 + * 参数说明 + * 返回值说明: java.util.Set + ***********************************/ + Set getOrgCodeByUser(String id); + + /********************************** + * 用途说明: 新增系统组织框架 + * 参数说明 sysOrganization + * 返回值说明: boolean + ***********************************/ + boolean addOrganization(SysOrganization sysOrganization); + + + /********************************** + * 用途说明: 修改系统组织框架 + * 参数说明 sysOrganization + * 返回值说明: boolean + ***********************************/ + boolean updateOrganization(SysOrganization sysOrganization); + + /********************************** + * 用途说明: 设置组织是否有效 + * 参数说明 sysOrganization + * 返回值说明: boolean + ***********************************/ + boolean setIsValid(String id, String isvaild); + + /********************************** + * 用途说明: 根据id删除系统组织框架 + * 参数说明 id + * 返回值说明: boolean + ***********************************/ + boolean deleteById(String id); +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/ISysRoleService.java b/riis-system/src/main/java/com/yfd/platform/system/service/ISysRoleService.java new file mode 100644 index 0000000..0180a8e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/ISysRoleService.java @@ -0,0 +1,75 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysRole; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统角色 服务类 + *

+ * + * + * @since 2021-12-15 + */ +public interface ISysRoleService extends IService { + + /*********************************** + * 用途说明:新增角色 + * 参数说明 + * sysRole 新增角色信息 + * 返回值说明: 是否新增成功 + ***********************************/ + boolean addRole(SysRole sysRole); + + /*********************************** + * 用途说明:删除角色用户 + * 参数说明 + * id 系统角色用户对照表id + * 返回值说明: 是否新增成功 + ***********************************/ + + boolean deleteRoleUsers(String roleid, String urserids); + + /*********************************** + * 用途说明:根据id删除角色 + * 参数说明 + *id 角色id + * 返回值说明: 是否删除成功 + ***********************************/ + void deleteById(String id); + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + List listRoleUsers(String orgid, String username, String status, String level, String rolename, String isvaild); + + + /*********************************** + * 用途说明:角色分配权限 + * 参数说明 + * id 角色id + * menuIds 权限id字符串 + * 返回值说明: 是否分配成功 + ***********************************/ + boolean setMenuById(String id, String menuIds); + + /********************************** + * 用途说明: 角色分配摄像头 + * 参数说明 id 角色id + * 参数说明 deviceIds 摄像头id + * 返回值说明: void + ***********************************/ + void setPatrolDevice(String id, String deviceIds); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/IUserService.java b/riis-system/src/main/java/com/yfd/platform/system/service/IUserService.java new file mode 100644 index 0000000..21fb729 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/IUserService.java @@ -0,0 +1,184 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysUser; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 系统用户 + *

+ * + * + * @since 2021-10-27 + */ +public interface IUserService extends IService { + + //获取当前用户账号及名称 + String getUsername(); + + //获取当前用户信息 + SysUser getUserInfo(); + /*********************************** + * 用途说明:获取当前用户账号与姓名 + * 返回值说明: 当前用户账号与姓名 + ************************************/ + Map getNameInfo(); + //获取当前用户信息带权限 + ResponseResult getLoginUserInfo(); + + /*********************************** + * 用途说明:新增用户 + * 参数说明 + *sysUser 新增用户对象 + * id 创建者id + * roleId 角色id + * 返回值说明: 提示字符串 + ************************************/ + Map addUser(SysUser sysUser, String roleids); + + /*********************************** + * 用途说明:查询系统用户 + * 参数说明 + *page 分页集合参数 + *orgid 所属组织 + *username 用户名称 + * mobile 手机号 + * status 状态 + * 返回值说明: 用户分页集合 + ************************************/ + List list(String total, String size, String orgid, String username, + String mobile, String status); + + /*********************************** + * 用途说明:根据ID查询用户详情 + * 参数说明 + *id 用户id + * 返回值说明: 用户表对象 + ************************************/ + Map getOneById(String id); + + /*********************************** + * 用途说明:根据ID修改用户 + * 参数说明 + *sysUser 用户对象 + *roleids 角色id + * 返回值说明: 是否更新成功 + ************************************/ + Map updateById(SysUser sysUser, String roleids); + + /*********************************** + * 用途说明:用户分配角色(多个) + * 参数说明 + *roleid 角色id + * userids 用户id数组 + * 返回值说明: 判断是否添加成功 + ************************************/ + boolean setUserRoles(String roleid, String userids); + + /*********************************** + * 用途说明:根据id删除用户 + * 参数说明 + *id 用户id + * 返回值说明: 判断是否删除成功 + ************************************/ + boolean deleteById(String id); + + /*********************************** + * 用途说明:重置用户密码(管理员) + * 参数说明 + *id 重置密码的 用户id + * 返回值说明: 判断是否重置成功 + ************************************/ + boolean resetPassword(String id) throws Exception; + + /*********************************** + * 用途说明:设置账号状态(管理员) + * 参数说明 + *id 用户id + * status 设置状态 + * 返回值说明: 判断是否设置成功 + ************************************/ + boolean setStatus(String id, String status); + + /*********************************** + * 用途说明:上传用户头像 + * 参数说明 + * id 用户id + * img 账号头像 + * 返回值说明: 判断是否上传 + ***********************************/ + boolean uploadAvatar(String id, MultipartFile img); + + /*********************************** + * 用途说明:新增系统角色用户对照表 对用户分配角色(单个) + * 参数说明 + * id 生成的id + * userid 用户id + * roleid 角色id + * 返回值说明: + ************************************/ + boolean addUserRoles(String roleid, String userid); + + //Page queryUsers(String orgid, String username, Page page); + Page> queryUsers(String orgid, String username,String status, Page page); + + /*********************************** + * 用途说明:根据ID批量删除用户 + * 参数说明 + *ids 用户id集合 + * 返回值说明: 判断是否删除成功 + ************************************/ + boolean deleteUserByIds(String ids); + + /*********************************** + * 用途说明:根据用户表id查询角色表所有角色id + * 参数说明 + * userId 用户id + * 返回值说明: + ************************************/ + List getRoleId(String userId); + + /*********************************** + * 用途说明:根据用户账号获取用户信息 + * 参数说明 + * username 用户账号 + * 返回值说明: 用户信息 + ************************************/ + SysUser getUserInfo(String username); + + /*********************************** + * 用途说明:检测用户输入密码是否与保存密码一致 + * 参数说明 + * username 用户账号 + * password 用户密码 + * 返回值说明: true + ************************************/ + boolean checkPassword(String username, String password); + + /*********************************** + * 用途说明:更新用户密码 + * 参数说明 + * username 用户账号 + * password 用户密码 + * 返回值说明: 密码加密后更新到数据库 + ************************************/ + boolean updatePassword(String username, String password) ; + + List getLevel(String id); + + /*********************************** + * 用途说明: 获取审计管理员用户 + * 参数说明 + * 返回值说明: java.util.List + ***********************************/ + List getAuditManagement(); + + boolean sendVerCodeToPhone(String phone, String sessionContext, String templateId, String[] templateParamSet); + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/MessageServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/MessageServiceImpl.java new file mode 100644 index 0000000..530eab1 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/MessageServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.mapper.MessageMapper; +import com.yfd.platform.system.service.IMessageService; +import org.springframework.stereotype.Service; + +/** + *

+ * 消息通知 服务实现类 + *

+ * + * + * @since 2023-03-19 + */ +@Service +public class MessageServiceImpl extends ServiceImpl implements IMessageService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/QuartzJobServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/QuartzJobServiceImpl.java new file mode 100644 index 0000000..59df0fa --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/QuartzJobServiceImpl.java @@ -0,0 +1,113 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.mapper.QuartzJobMapper; +import com.yfd.platform.system.service.IQuartzJobService; +import com.yfd.platform.utils.QuartzManage; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + *

+ * 定时任务 服务实现类 + *

+ * + * + * @since 2023-03-19 + */ +@Service +public class QuartzJobServiceImpl extends ServiceImpl implements IQuartzJobService { + + @Resource + private QuartzJobMapper quartzJobMapper; + + @Resource + private QuartzManage quartzManage; + + /********************************** + * 用途说明: 新增定时任务 + * 参数说明 quartzJob 定时对象 + * 返回值说明: boolean 是否成功 + ***********************************/ + @Override + public boolean addQuartzJob(QuartzJob quartzJob) { + // 生成序号 + int orderNo = this.count() + 1; + quartzJob.setOrderno(orderNo); + return this.save(quartzJob); + } + + /********************************** + * 用途说明: 删除定时任务 + * 参数说明 id id + * 返回值说明: boolean 是否成功 + ***********************************/ + @Override + public boolean deleteQuartzJob(String id) { + String[] split = id.split(","); + Set ids = Arrays.stream(split).collect(Collectors.toSet()); + for (String s : ids) { + QuartzJob quartzJob = this.getById(s); + quartzManage.deleteJob(quartzJob); + this.removeById(s); + } + + // 查询所有定时任务 + List list = + this.list(new LambdaQueryWrapper().orderByAsc(QuartzJob::getOrderno)); + // 更新序号 + for (int i = 0; i < list.size(); i++) { + QuartzJob quartzJob = list.get(i); + quartzJob.setOrderno(i + 1); + this.updateById(quartzJob); + } + return true; + } + + /********************************** + * 用途说明: 拖动修改定时任务顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Override + public boolean changeDictOrder(String fromID, String toID) { + QuartzJob fromQuartzJob = + quartzJobMapper.selectById(fromID); + QuartzJob toQuartzJob = quartzJobMapper.selectById(toID); + // 如果数据字典不存在拖动失败 + if (fromQuartzJob == null || toQuartzJob == null) { + return false; + } + Integer fromOrderNo = fromQuartzJob.getOrderno(); + Integer toOrderNo = toQuartzJob.getOrderno(); + // 如果数据字典的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + // 将顺序号放入字典对象中 + fromQuartzJob.setOrderno(toOrderNo); + toQuartzJob.setOrderno(fromOrderNo); + // 更改顺序号 + boolean fromBool = this.updateById(fromQuartzJob); + boolean toBool = this.updateById(toQuartzJob); + return fromBool && toBool; + } + + /********************************** + * 用途说明: 执行定时任务 + * 参数说明 id id + * 返回值说明: void + ***********************************/ + @Override + public void execution(QuartzJob quartzJob) { + quartzManage.runJobNow(quartzJob); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/RolePatroldeviceServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/RolePatroldeviceServiceImpl.java new file mode 100644 index 0000000..0dc1872 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/RolePatroldeviceServiceImpl.java @@ -0,0 +1,20 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.RolePatroldevice; +import com.yfd.platform.system.mapper.RolePatroldeviceMapper; +import com.yfd.platform.system.service.IRolePatroldeviceService; +import org.springframework.stereotype.Service; + +/** + *

+ * 系统角色摄像头对照 服务实现类 + *

+ * + * + * @since 2023-04-23 + */ +@Service +public class RolePatroldeviceServiceImpl extends ServiceImpl implements IRolePatroldeviceService { + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysConfigServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..be15264 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,88 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.modules.basedata.domain.AlgorithmModel; +import com.yfd.platform.system.domain.SysConfig; +import com.yfd.platform.system.mapper.SysConfigMapper; +import com.yfd.platform.system.service.ISysConfigService; +import com.yfd.platform.utils.FileUtil; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.util.Objects; + +/** + *

+ * 系统全局配置 服务实现类 + *

+ * + * + * @since 2022-01-19 + */ +@Service +public class SysConfigServiceImpl extends ServiceImpl implements ISysConfigService { + + @Resource + private UserServiceImpl currentUser; + + @Resource + private HttpServerConfig httpServerConfig; + + /********************************** + * 用途说明: 上传系统操作手册 + * 参数说明 file 文件域 + * 返回值说明: void + ***********************************/ + @Override + public void uploadModelFile(MultipartFile file) { + // 文件存储地址 + String fileName = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(file.getOriginalFilename()); + // 上传文件 + String name = + Objects.requireNonNull(FileUtil.upload(file, httpServerConfig.getDocumentPath(), fileName)).getName(); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.set(SysConfig::getCustom2, name); + this.update(updateWrapper); + } + + /********************************** + * 用途说明: 下载操作手册 + * 参数说明 fileName 文件名称 + * 参数说明 response + * 返回值说明: void + ***********************************/ + @Override + public void downloadConfigFile(String fileName, HttpServletResponse response) { + boolean exist = FileUtil.exist(httpServerConfig.getDocumentPath() + fileName); + if (!exist) { + throw new RuntimeException("文件不存在"); + } + File file = new File(httpServerConfig.getDocumentPath() + fileName); + FileUtil.downloadFile(null, response, file, false); + } + + /********************************** + * 用途说明: 删除文件 + * 参数说明 id 配置id + * 参数说明 fileName 文件名称 + * 返回值说明: boolean + ***********************************/ + @Override + public boolean deleteConfigFile(String id, String fileName) { + boolean exist = FileUtil.exist(httpServerConfig.getDocumentPath() + fileName); + if (!exist) { + throw new RuntimeException("删除失败"); + } + FileUtil.del(httpServerConfig.getDocumentPath() + fileName); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SysConfig::getId, id).set(SysConfig::getCustom2, ""); + return this.update(updateWrapper); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDataBackupServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDataBackupServiceImpl.java new file mode 100644 index 0000000..d0d2f3d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDataBackupServiceImpl.java @@ -0,0 +1,176 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.config.DatabaseConfigReader; +import com.yfd.platform.system.domain.SysDataBackup; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.mapper.SysDataBackupMapper; +import com.yfd.platform.system.service.ISysDataBackupService; +import com.yfd.platform.utils.FileUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

+ * 系统数据备份恢复记录 服务实现类 + *

+ * + * + * @since 2023-08-31 + */ +@Service +@Slf4j +public class SysDataBackupServiceImpl extends ServiceImpl implements ISysDataBackupService { + + @Resource + private DatabaseConfigReader databaseConfigReader; + + @Override + public Page getBackFileList(String filename, String datascope, String backuptype, String startDate + , String endDate, Page page) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + if (StrUtil.isNotBlank(filename)) { + queryWrapper.like(SysDataBackup::getFilename, filename); + } + // 没有传username就不按此条件查询 + if (StrUtil.isNotBlank(datascope)) { + queryWrapper.eq(SysDataBackup::getDatascope, datascope); + } + // + if (StrUtil.isNotBlank(backuptype)) { + queryWrapper.eq(SysDataBackup::getBackuptype, backuptype); + } + + + if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) { + DateTime parseStartDate = DateUtil.parse(startDate); + DateTime parseEndDate = DateUtil.parse(endDate); + DateTime dateTime = DateUtil.offsetDay(parseEndDate, 1); + queryWrapper.ge(SysDataBackup::getBackupdate, parseStartDate).lt(SysDataBackup::getBackupdate, dateTime); + } + queryWrapper.orderByDesc(SysDataBackup::getBackupdate); + return this.page(page, queryWrapper); + } + + @Override + public boolean backupDataTable(String datascope, String backuptype) { + try { + String url=databaseConfigReader.getDatabaseUrl(); + Pattern pattern = Pattern.compile("jdbc:mysql://(.*):(\\d+)/(\\w+)\\?"); + Matcher matcher = pattern.matcher(url); + + String ipAddress = ""; + int port = 0; + String databaseName = ""; + if (matcher.find()) { + ipAddress = matcher.group(1); + port = Integer.parseInt(matcher.group(2)); + databaseName = matcher.group(3); + } else { + log.error("URL format does not match."); + return false; + } + + String backupPath=databaseConfigReader.getBackupPath(); + + StringBuilder command = new StringBuilder("mysqldump") + .append(" --host=").append(ipAddress) + .append(" --port=").append(port) + .append(" --user=").append(databaseConfigReader.getDatabaseUsername()) + .append(" --password=").append(databaseConfigReader.getDatabasePassword()) + .append(" ").append(databaseName); + + String[] tablesToBackup = {}; // 要备份的数据表名称数组 + if(datascope.equals("基础数据")){ + tablesToBackup = new String[]{"iis_substation", "iis_substation_area","iis_substation_bay","iis_substation_maindevice","iis_substation_component","iis_substation_device","iis_substation_patroldevice","iis_algorithm_model","mon_device","mon_device_channel", + "sys_config","sys_dictionary","sys_dictionary_items","sys_menu","sys_organization","sys_quartz_job","sys_role","sys_user","sys_role_menu","sys_role_patroldevice","sys_role_users"}; + for (String tableName : tablesToBackup) { + + command.append(" ").append(tableName); + } + }else if(datascope.equals("业务数据")){ + tablesToBackup = new String[]{"iis_task", "iis_task_todo", "iis_task_result", "iis_alarm_log", "iis_examine_plan","iis_linkage_signal","iis_weather_log"}; + for (String tableName : tablesToBackup) { + command.append(" ").append(tableName); + } + } + + String filename= String.format("%s-%s-%s.sql",DateUtil.format(DateUtil.date(),"yyyyMMddHHmmss"),datascope,backuptype); + FileUtil.touch(backupPath+filename);//创建多级目录 + command.append(" --result-file=").append(backupPath+filename); + + ProcessBuilder processBuilder = new ProcessBuilder(command.toString().split(" ")); + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + + if (exitCode == 0) { + SysDataBackup sysDataBackup=new SysDataBackup(); + sysDataBackup.setDatascope(datascope); + sysDataBackup.setFilename(filename); + sysDataBackup.setBackuptype(backuptype); + sysDataBackup.setBackupdate(DateUtil.toLocalDateTime(DateUtil.date())); + File file = new File(backupPath+filename); + if (file.exists()) { + long fileSizeInBytes = file.length(); + long fileSizeInKB = fileSizeInBytes / 1024; + sysDataBackup.setFilesize(fileSizeInKB+"KB"); + } + this.save(sysDataBackup); + log.info("Backup completed successfully."); + } else { + log.error("Backup failed."); + } + } catch (InterruptedException | IOException e) { + log.error(e.getMessage());; + } + return true; + } + + @Override + public boolean recoverDataTable(String id) { + SysDataBackup sysDataBackup = this.getById(id); + String backupPath = databaseConfigReader.getBackupPath(); + String filefullname = backupPath + sysDataBackup.getFilename(); + if (StrUtil.isNotEmpty(filefullname)) { + } + return true; + } + + /********************************** + * 用途说明: 下载数据库文件 + * 参数说明 id 数据表备份文件记录id + * 返回值说明: boolean 是否成功 + ***********************************/ + @Override + public boolean downloadDataTable(String id, HttpServletResponse response) { + SysDataBackup sysDataBackup = this.getById(id); + String backupPath = databaseConfigReader.getBackupPath(); + String filefullname = backupPath + sysDataBackup.getFilename(); + if (StrUtil.isNotEmpty(filefullname)) { + File file = new File(filefullname); + FileUtil.downloadFile(null, response, file, false); + } + return true; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryItemsServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryItemsServiceImpl.java new file mode 100644 index 0000000..26f98a5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryItemsServiceImpl.java @@ -0,0 +1,147 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.mapper.SysDictionaryMapper; +import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.FileUtil; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 数据字典明细 服务实现类 + *

+ * + * + * @since 2023-03-08 + */ +@Service +public class SysDictionaryItemsServiceImpl extends ServiceImpl implements ISysDictionaryItemsService { + + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; + + @Resource + private SysDictionaryMapper sysDictionaryMapper; + + /********************************** + * 用途说明: 分页查询字典项信息 + * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @Override + public Page getDictItemPage(String dictId, + String itemName, + Page page) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + if (StrUtil.isNotBlank(itemName)) { + queryWrapper.like(SysDictionaryItems::getDictName, itemName); + } + queryWrapper.eq(SysDictionaryItems::getDictId, dictId).orderByAsc(SysDictionaryItems::getOrderNo); + return sysDictionaryItemsMapper.selectPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 增加字典项 + * 参数说明 sysDictionaryItems 字典项信息 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回增加成功或者失败 + ***********************************/ + @Override + public boolean addDictionaryItem(SysDictionaryItems sysDictionaryItems) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId,sysDictionaryItems.getDictId()); + int orderNo = this.count(queryWrapper) + 1; + // 添加顺序号 + sysDictionaryItems.setOrderNo(orderNo); + return this.save(sysDictionaryItems); + } + + /********************************** + * 用途说明: 拖动修改字典项顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Override + public boolean changeItemOrder(String fromID, String toID) { + SysDictionaryItems fromSysDictionaryItems = + sysDictionaryItemsMapper.selectById(fromID); + SysDictionaryItems toSysDictionaryItems = + sysDictionaryItemsMapper.selectById(toID); + // 如果数据字典项不存在拖动失败 + if (fromSysDictionaryItems == null || toSysDictionaryItems == null) { + return false; + } + Integer fromOrderNo = fromSysDictionaryItems.getOrderNo(); + Integer toOrderNo = toSysDictionaryItems.getOrderNo(); + // 如果数据字典的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + // 将顺序号放入字典对象中 + fromSysDictionaryItems.setOrderNo(toOrderNo); + toSysDictionaryItems.setOrderNo(fromOrderNo); + // 更改顺序号 + boolean fromBool = this.updateById(fromSysDictionaryItems); + boolean toBool = this.updateById(toSysDictionaryItems); + return fromBool && toBool; + + } + + /********************************** + * 用途说明: 导出数据字典项数据 + * 参数说明 sysDictionaryItemsList 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或失败 + ***********************************/ + @Override + public void exportExcel(List sysDictionaryItems, + HttpServletResponse response) { + try { + List> list = new LinkedList<>(); + for (SysDictionaryItems sysDictionaryItem : sysDictionaryItems) { + Map map = new LinkedHashMap<>(); + map.put("项编号", sysDictionaryItem.getItemCode()); + map.put("项名称", sysDictionaryItem.getDictName()); + map.put("父编码", sysDictionaryItem.getParentCode()); + map.put("备注", sysDictionaryItem.getCustom1()); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + + /********************************** + * 用途说明: 获取类型 + * 参数说明 dictcode + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getDeviceByType(String dictcode) { + SysDictionary sysDictionary = + sysDictionaryMapper.selectOne(new LambdaQueryWrapper().eq(SysDictionary::getDictCode, + dictcode)); + String id = sysDictionary.getId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionaryItems::getDictId, id); + queryWrapper.select(SysDictionaryItems::getId, SysDictionaryItems::getItemCode, + SysDictionaryItems::getDictName,SysDictionaryItems::getCustom1, SysDictionaryItems::getCustom2,SysDictionaryItems::getOrderNo); + List> maps = sysDictionaryItemsMapper.selectMaps(queryWrapper); + List> itemCode = maps.stream().sorted(Comparator.comparing(m -> Integer.valueOf(m.get( + "orderno").toString()))).collect(Collectors.toList()); + return itemCode; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryServiceImpl.java new file mode 100644 index 0000000..6d8492e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysDictionaryServiceImpl.java @@ -0,0 +1,113 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysDictionary; +import com.yfd.platform.system.domain.SysDictionaryItems; +import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; +import com.yfd.platform.system.mapper.SysDictionaryMapper; +import com.yfd.platform.system.service.ISysDictionaryService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +/** + *

+ * 数据字典表 服务实现类 + *

+ * + * + * @since 2023-03-08 + */ +@Service +public class SysDictionaryServiceImpl extends ServiceImpl implements ISysDictionaryService { + + @Resource + private SysDictionaryMapper sysDictionaryMapper; + + @Resource + private SysDictionaryItemsMapper sysDictionaryItemsMapper; + + /********************************** + * 用途说明: 获取数据字典列表 + * 参数说明 dictType 字典类型 + * 返回值说明: 返回字典列表集合 + ***********************************/ + @Override + public List getDictList(String dictType) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysDictionary::getDictType, dictType).orderByAsc(SysDictionary::getOrderNo); + return sysDictionaryMapper.selectList(queryWrapper); + } + + /********************************** + * 用途说明: 新增字典 + * 参数说明 sysDictionary 字典对象 + * 返回值说明: 返回增加成功或者失败 + ***********************************/ + @Override + public boolean addDict(SysDictionary sysDictionary) { + //int orderNo = this.count() + 1; + Integer maxNo = + sysDictionaryMapper.selectMaxNo(sysDictionary.getDictType()); + if (maxNo == null) { + maxNo = 0; + } + // 添加顺序号 + sysDictionary.setOrderNo(maxNo + 1); + return this.save(sysDictionary); + } + + /********************************** + * 用途说明: 根据ID删除字典 + * 参数说明 id 字典ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回删除结果成功或者失败 + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteDictById(String id) { + // 根据字典编码查询字典项中是否关联 + boolean isok=true; + QueryWrapper Wrapper = new QueryWrapper<>(); + Wrapper.eq("dictid", id); + if(sysDictionaryItemsMapper.delete(Wrapper)>0) { + isok=true; + } + return isok&&this.removeById(id); + } + + /********************************** + * 用途说明: 拖动修改字典顺序 + * 参数说明 fromID 当前ID toID 到达ID + * 返回值说明: com.yfd.platform.config.ResponseResult 返回拖动成功或者失败 + ***********************************/ + @Override + public boolean changeDictOrder(String fromID, String toID) { + SysDictionary fromSysDictionary = + sysDictionaryMapper.selectById(fromID); + SysDictionary toSysDictionary = sysDictionaryMapper.selectById(toID); + // 如果数据字典不存在拖动失败 + if (fromSysDictionary == null || toSysDictionary == null) { + return false; + } + Integer fromOrderNo = fromSysDictionary.getOrderNo(); + Integer toOrderNo = toSysDictionary.getOrderNo(); + // 如果数据字典的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + // 将顺序号放入字典对象中 + fromSysDictionary.setOrderNo(toOrderNo); + toSysDictionary.setOrderNo(fromOrderNo); + // 更改顺序号 + boolean fromBool = this.updateById(fromSysDictionary); + boolean toBool = this.updateById(toSysDictionary); + return fromBool && toBool; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysLogServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysLogServiceImpl.java new file mode 100644 index 0000000..bc8c54f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysLogServiceImpl.java @@ -0,0 +1,258 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.mapper.SysLogMapper; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + *

+ * 系统操作日志 服务实现类 + *

+ * + * + * @since 2023-03-08 + */ +@Service +public class SysLogServiceImpl extends ServiceImpl implements ISysLogService { + + @Resource + private SysLogMapper sysLogMapper; + + /********************************** + * 用途说明: 分页查询日志信息 + * 参数说明 pageNum(页码数)、pageSize(页大小,如果是固定页大小可不传)、username(用户名)、(optType) + * 操作类型、startDate(开始日期)、endDate(结束日期) + * 返回值说明: com.yfd.platform.config.ResponseResult 返回分页查询结果 + ***********************************/ + @Override + public Page getLogList(String username, String optType, String requestip, String module, String type, + String startDate, + String endDate, String orderfield, Page page) { + + LambdaQueryWrapper queryWrapper = getQueryWrapper(username, optType, requestip, module, type, + startDate, endDate, orderfield); + // sysLogMapper.getLogList(username, optType, requestip, module, type, startDate, endDate, orderfield); + return sysLogMapper.selectPage(page, queryWrapper); + } + + /********************************** + * 用途说明: 导出日志数据 + * 参数说明 sysLogs 所需导出的字典项集合 + * 返回值说明: com.yfd.platform.config.ResponseResult 返回导出成功或者失败 + ***********************************/ + @Override + public void exportExcel(String username, String optType,String requestip,String module,String type, + String startDate, + String endDate, String orderfield, + HttpServletResponse response) { + try { + LambdaQueryWrapper queryWrapper =getQueryWrapper(username,optType,requestip,module,type,startDate,endDate,orderfield); + List sysLogs=sysLogMapper.selectList(queryWrapper); + List> list = new LinkedList<>(); + for (SysLog sysLog : sysLogs) { + Map map = new LinkedHashMap<>(); + map.put("操作账号", sysLog.getUsercode()); + map.put("用户姓名", sysLog.getUsername()); + map.put("IP地址", sysLog.getRequestip()); + map.put("浏览器", sysLog.getBrowser()); + map.put("日志类型", sysLog.getOpttype()); + map.put("模块名称", sysLog.getModule()); + map.put("日志描述", sysLog.getDescription()); + Timestamp logTime = sysLog.getLogtime(); + String dateTime = ""; + if (logTime != null) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + dateTime = df.format(logTime); + } + map.put("操作日期", dateTime); + list.add(map); + } + FileUtil.downloadExcel(list, response); + } catch (Exception e) { + log.error(e.getMessage());; + } + + } + + //定义查询条件和排序字段 + private LambdaQueryWrapper getQueryWrapper(String username, String optType, String requestip, + String module, String type, + String startDate, + String endDate, String orderfield) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 没有传username就不按此条件查询 + if (StrUtil.isNotBlank(username)) { + queryWrapper.like(SysLog::getUsername, username).or().like(SysLog::getUsercode, username); + } + // + if (StrUtil.isNotBlank(requestip)) { + queryWrapper.eq(SysLog::getRequestip, requestip); + } + // + if (StrUtil.isNotBlank(module)) { + queryWrapper.eq(SysLog::getModule, module); + } + if (StrUtil.isNotBlank(type)) { + queryWrapper.eq(SysLog::getType, type); + } + // + if (StrUtil.isNotBlank(optType)) { + queryWrapper.eq(SysLog::getOpttype, optType); + } + + if (StrUtil.isNotBlank(startDate) && StrUtil.isNotBlank(endDate)) { + DateTime parseStartDate = DateUtil.parse(startDate); + DateTime parseEndDate = DateUtil.parse(endDate); + DateTime dateTime = DateUtil.offsetDay(parseEndDate, 1); + queryWrapper.ge(SysLog::getLogtime, parseStartDate).lt(SysLog::getLogtime, dateTime); + } + if (StrUtil.isNotEmpty(orderfield)) { + if (orderfield.equals("操作账号")) { + queryWrapper.orderByDesc(SysLog::getUsercode); + } else if (orderfield.equals("用户姓名")) { + queryWrapper.orderByDesc(SysLog::getUsername); + } else if (orderfield.equals("IP地址")) { + queryWrapper.orderByDesc(SysLog::getRequestip); + } else if (orderfield.equals("日志类型")) { + queryWrapper.orderByDesc(SysLog::getOpttype); + }else if(orderfield.equals("模块名称")){ + queryWrapper.orderByDesc(SysLog::getModule); + }else if(orderfield.equals("操作时间")){ + queryWrapper.orderByDesc(SysLog::getLogtime); + } + } + else{ + queryWrapper.orderByDesc(SysLog::getLogtime); + } + return queryWrapper; + } + + /********************************** + * 用途说明: 新增日志 + * 参数说明 nickname 用户名 + * 参数说明 username 用户账号 + * 参数说明 browser 浏览器 + * 参数说明 ip 本机Ip地址 + * 参数说明 joinPoint 连接点 + * 参数说明 log 日志信息 + * 返回值说明: void + ***********************************/ + @Override + public void save(String nickname, String username, String browser, + String ip, + ProceedingJoinPoint joinPoint, SysLog log) { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + Log aopLog = method.getAnnotation(Log.class); + // 方法路径 + String methodName = + joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()"; + // 描述 + if (log != null) { + log.setDescription(aopLog.value()); + log.setModule(aopLog.module()); + log.setType(aopLog.type()); + } + assert log != null; + log.setUsercode(username); + log.setRequestip(ip); + log.setMethod(methodName); + log.setUsername(nickname); + log.setParams(getParameter(method, joinPoint.getArgs())); + log.setBrowser(browser); + String operationtype = getOperationtype(signature.getName()); + log.setOpttype(operationtype); + log.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogMapper.insert(log); + } + + @Override + public void deleteLogs() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.lt(SysLog::getLogtime, DateUtil.offsetMonth(DateUtil.date(), -7)); + sysLogMapper.delete(queryWrapper); + } + + /** + * 根据方法和传入的参数获取请求参数 + */ + private String getParameter(Method method, Object[] args) { + List argList = new ArrayList<>(); + Parameter[] parameters = method.getParameters(); + for (int i = 0; i < parameters.length; i++) { + //将RequestBody注解修饰的参数作为请求参数 + AnnotatedType type = parameters[i].getAnnotatedType(); + + RequestBody requestBody = + parameters[i].getAnnotation(RequestBody.class); + if (requestBody != null) { + argList.add(args[i]); + } + + //将RequestParam注解修饰的参数作为请求参数 + RequestParam requestParam = + parameters[i].getAnnotation(RequestParam.class); + if (requestParam != null) { + Map map = new HashMap<>(); + String key = parameters[i].getName(); + if (!StringUtils.isEmpty(requestParam.value())) { + key = requestParam.value(); + } + map.put(key, args[i]); + argList.add(map); + } + } + if (argList.size() == 0) { + return ""; + } + return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : + JSONUtil.toJsonStr(argList); + } + + public static String getOperationtype(String value) { + String type = ""; + if (value.contains("get") || value.contains("select")) { + type = "查询(select)"; + } else if (value.contains("add") || value.contains("insert")) { + type = "添加(insert)"; + } else if (value.contains("update") || value.contains("upd") || value.contains("change") || value.contains("set")) { + type = "修改(update)"; + } else if (value.contains("delete") || value.contains("del")) { + type = "删除(delete)"; + } else if (value.contains("dowload")) { + type = "下载(dowload)"; + } else if (value.contains("import")) { + type = "导入(import)"; + } else if (value.contains("word")) { + type = "word转pdf(wordToPdf)"; + } else { + type = "其他(other)"; + } + return type; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysMenuServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..527e8bb --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,693 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysMenu; +import com.yfd.platform.system.mapper.SysMenuMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysMenuService; +import com.yfd.platform.utils.FileUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + *

+ * 菜单及按钮 服务实现类 + *

+ * + * + * @since 2021-12-15 + */ +@Service +@Transactional +public class SysMenuServiceImpl extends ServiceImpl implements ISysMenuService { + + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private UserServiceImpl currentUser; + + @Resource + private SysRoleMapper sysRoleMapper; + + //菜单图片路径 + @Value("${file-space.system}") + private String sysetmPath; + + /*********************************** + * 用途说明:查询菜单及按钮树状图 + * 参数说明 + * systemcode 系统 + *isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @Override + public List> getMenuButtonTree(String systemcode, + String name, + String isdisplay) { + List> listMap=null; + if(StrUtil.isEmpty(name)){//不带名称查询,返回树结构 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parentid", "0").eq("systemcode", systemcode).orderByAsc("orderno"); + listMap = this.listMaps(queryWrapper); + + for (int i = 0; i < listMap.size(); i++) { + //查询下一子集 + List> childList = child(listMap.get(i).get( + "id").toString(), systemcode, name, null, null); + listMap.get(i).put("children", childList); //添加新列 子集 + } + }else{ //根据菜单名称查询,直接返回类别 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like("name", name).eq("systemcode", systemcode).orderByAsc("name"); + listMap = this.listMaps(queryWrapper); + } + + + + return listMap; + } + + /*********************************** + * 用途说明:获取菜单结构树(不含按钮) + * 参数说明 + * systemcode 系统 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @Override + public List> getMenuTree(String systemcode, + String name, + String isdisplay) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotEmpty(isdisplay)) { + queryWrapper.eq("isdisplay", isdisplay); + } else { + queryWrapper.eq("isdisplay", 1); + } + + //根据系统 ,类型不为2 显示,序号 正序排序 + queryWrapper.eq("parentid", "0").eq("systemcode", systemcode).ne( + "type", "2").orderByAsc("orderno"); + List> listMap = this.listMaps(queryWrapper); + for (int i = 0; i < listMap.size(); i++) { + List> childList = child(listMap.get(i).get( + "id").toString(), systemcode, name, isdisplay, "2");//查询下一子集 + listMap.get(i).put("children", childList); //添加新列 子集 + } + return listMap; + } + + /*********************************** + * 用途说明:查询菜单及按钮树状图 + * 参数说明 + * parentid 上级id + *systemcode 系统 + * isdisplay 是否显示 + * type 按钮 + * 返回值说明: 菜单结构树集合 + ***********************************/ + private List> child(String parentid, + String systemcode, String name, + String isdisplay, String type) { + List> listMap = new ArrayList<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parentid", parentid).eq("systemcode", systemcode); + //根据上级id 系统 查询 + if (StrUtil.isNotEmpty(type)) { + queryWrapper.ne("type", type); + } + + if (StrUtil.isNotEmpty(name)) { //根据菜单名称查询 + queryWrapper.like("name", name); + } + listMap = this.listMaps(queryWrapper.orderByAsc("orderno")); + if (listMap.size() > 0) { //判断是否存在子集 + for (int i = 0; i < listMap.size(); i++) { //遍历表数据 + List> childList = + child(listMap.get(i).get("id").toString(), systemcode + , name, isdisplay, type); //循环获取下一子集 + listMap.get(i).put("children", childList); //添加新列 子集 + } + } + + return listMap; + } + + /*********************************** + * 用途说明:新增菜单及按钮 + * 参数说明 + * sysMenu 菜单或按钮表对象 + * 返回值说明: 是否添加成功提示 + ***********************************/ + @Override + public boolean addMenu(SysMenu sysMenu) { + String parentId = sysMenu.getParentid(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + //根据上级id 查询到总数 并累加 + int orderno = this.count(queryWrapper.eq("parentid", + parentId)) + 1; + // 生成排序号 + // 生成编号 + QueryWrapper queryMaxCode = new QueryWrapper<>(); + queryMaxCode.eq("parentid", + parentId); + // 查询最大的编号 + List maxList = this.listObjs(queryMaxCode.select("max(code) " + + "code").eq("systemcode", sysMenu.getSystemcode())); + SysMenu parentMenu = sysMenuMapper.selectById(parentId); + // 最大编号转换成int类型 + String maxCode = maxList.size() > 0 ? maxList.get(0).toString() : "0"; + int max = ObjectUtil.isEmpty(maxList) ? 0 : + Integer.parseInt(maxCode); + DecimalFormat df; + if ("0".equals(sysMenu.getParentid())) { + df = new DecimalFormat("00"); + } else if (parentMenu.getCode().length() == 2) { + df = new DecimalFormat("0000"); + } else { + df = new DecimalFormat("000000"); + } + //DecimalFormat df = new DecimalFormat("00"); + //int parentCode = Integer.parseInt(parentMenu.getCode()); + String parentCode = ""; + if (parentMenu != null) { + parentCode = parentMenu.getCode(); + } + // 生成的新编号 年月日+4位编号 + String code; + if (max > 0) { + code = df.format(max + 1); + } else { + max = max + 1; + if (StrUtil.isBlank(parentCode)) { + parentCode = "0" + max; + } else { + int i = Integer.parseInt(parentCode); + parentCode = i + "0" + max; + } + + int format = Integer.parseInt(parentCode); + code = df.format(format); + } + + // 判断是否显示字段 是否填写 为空 + if (StrUtil.isEmpty(sysMenu.getIsdisplay())) { + // 默认设置成 1 显示 + sysMenu.setIsdisplay("1"); + } + // 判断是否填写父级id 为空 默认设置成 0 + if (StrUtil.isEmpty(sysMenu.getParentid())) { + sysMenu.setParentid("0"); + } + // 添加编号 + sysMenu.setCode(code); + // 添加排序号 + sysMenu.setOrderno(orderno); + // 添加最近修改人 + sysMenu.setLastmodifier(currentUser.getUsername()); + // 添加最近修改时间 + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + return this.save(sysMenu); + } + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * id 上传图标id + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + @Override + public boolean uploadIcon(String id, MultipartFile icon) { + //根据id查询 + SysMenu sysMenu = this.getById(id); + //图片路径 + String iconPath = sysetmPath + "menu" + File.separator; + String iconname = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(icon.getOriginalFilename()); + //上传图标并获取图标名称 (图片改为png格式) + String filename = + FileUtil.upload(icon, iconPath, iconname).getName(); + //更新图标名称 + sysMenu.setIcon(filename); + //添加最近修改人 + sysMenu.setLastmodifier(currentUser.getUsername()); + //添加最近修改时间 + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //更新数据 + boolean isOk = this.updateById(sysMenu); + return isOk; + } + + /*********************************** + * 用途说明:根据id删除单个图标 + * 参数说明 + * id 删除图标id + * icon 图标名称 + * 返回值说明: 是否删除成功 + ***********************************/ + @Override + public boolean deleteIcon(String id) { + //根据id查询 + SysMenu sysMenu = this.getById(id); + //图片路径 + // String iconname = + // System.getProperty("user.dir") + "\\riis-system\\src\\main" + + // "\\resources\\static\\icon" + File.separator + sysMenu.getIcon(); + String iconname = sysetmPath.replace("\\", "/") + "menuicon\\"+ File.separator + sysMenu.getIcon(); + //更新图标名称 + sysMenu.setIcon(""); + //添加最近修改人 + sysMenu.setLastmodifier(currentUser.getUsername()); + //添加最近修改时间 + sysMenu.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //更新数据 + boolean isOk = this.updateById(sysMenu); + //更新成功 删除图片 + if (isOk == true) { + FileUtil.del(iconname); + } + return isOk; + } + + /*********************************** + * 用途说明:菜单及按钮序号排序 + * 参数说明 + * parentid 上级id + * id + * orderno 更改后序号 + * 返回值说明: 是否更新成功 + ***********************************/ + @Override + public boolean moveOrderno(String parentid, String id, int orderno) { + boolean ok = true; + SysMenu sysMenu = this.getById(id); //根据id查询原顺序号 + if (sysMenu.getOrderno() > orderno) { + ok = sysMenuMapper.upMoveOrderno(parentid, sysMenu.getOrderno(), + orderno); //根据 父级id 小于原序号 大于等于更改序号 + } else { + ok = sysMenuMapper.downMoveOrderno(parentid, sysMenu.getOrderno() + , orderno); //根据 父级id 大于原序号 小于等于更改序号 + } + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + ok = ok && this.update(updateWrapper.eq("id", id).set("orderno", + orderno)); //根据 id修改序号 + return ok; + } + + /*********************************** + * 用途说明:根据id删除菜单或按钮 + * 参数说明 + * id 删除列的id + * 返回值说明: 是否删除成功 + ***********************************/ + @Override + public boolean deleteById(String id) { + //根据id查询 + SysMenu sysMenu = this.getById(id); + //图片路径 + String iconname = + sysetmPath + "menu" + File.separator + sysMenu.getIcon(); + //删除图标 + new File(iconname).delete(); + //根据id删除 + boolean isOk = this.removeById(id); + //删除成功同步更新表数据 + if (isOk) { + //1 创建list集合,用于封装所有删除目录或菜单id值 + List idList = new ArrayList<>(); + this.selectPermissionChildById(id, idList); + if (idList.size() > 0) { + sysMenuMapper.deleteBatchIds(idList); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + //根据上级id 查询 根据 orderno 正序排序 + queryWrapper.eq("parentid", sysMenu.getParentid()).orderByAsc( + "orderno"); + List list = this.list(queryWrapper); + for (int i = 0; i < list.size(); i++) { + SysMenu menu = list.get(i); + //更新序列号 + menu.setOrderno(i + 1); + } + //更新表数据 + this.updateBatchById(list); + } + return isOk; + } + + //2 根据当前菜单id,查询菜单里面子菜单id,封装到list集合 + private void selectPermissionChildById(String id, List idList) { + //查询菜单里面子菜单id + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("parentid", id); + wrapper.select("id"); + List childIdList = baseMapper.selectList(wrapper); + //把childIdList里面菜单id值获取出来,封装idList里面,做递归查询 + childIdList.stream().forEach(item -> { + //封装idList里面 + idList.add(item.getId()); + //递归查询 + this.selectPermissionChildById(item.getId(), idList); + }); + } + + /********************************** + * 用途说明: 调换菜单或按钮的位置 + * 参数说明 upperId 选中的菜单Id + * 参数说明 belowId 切换的菜单Id + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional + public boolean changeOderNoById(String fromId, String toId) { + SysMenu fromSysMenu = this.getById(fromId); + SysMenu toSysMenu = this.getById(toId); + // 如果菜单或按钮不存在拖动失败 + if (fromSysMenu == null || toSysMenu == null) { + return false; + } + Integer fromOrderNo = fromSysMenu.getOrderno(); + Integer toOrderNo = toSysMenu.getOrderno(); + // 如果菜单或按钮的顺序号不存在拖动失败 + if (fromOrderNo == null || toOrderNo == null) { + return false; + } + fromSysMenu.setOrderno(toOrderNo); + toSysMenu.setOrderno(fromOrderNo); + boolean fromBool = this.updateById(fromSysMenu); + boolean toBool = this.updateById(toSysMenu); + return fromBool && toBool; + } + + /********************************** + * 用途说明: 根据用户id获取菜单树 + * 参数说明 id 用户id + * 返回值说明: 返回菜单树 + ***********************************/ + @Override + public List> getMenuTree(String id) { + // 根据id获取菜单 + List> list; + if (StrUtil.isBlank(id)) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + list = this.listMaps(queryWrapper.eq(SysMenu::getIsdisplay, "1").ne(SysMenu::getType, "2").eq(SysMenu::getSystemcode, "1").orderByAsc(SysMenu::getOrderno)); + } else { + list = sysMenuMapper.selectMenuByType(id,"1"); + } + // 将菜单转换成树 + List> sysMenus = buildTreeLeft(list); + return sysMenus; + } + + /*********************************** + * 用途说明:权限分配 + * 参数说明 + * systemcode 系统 + * name 名称 + * isdisplay 是否显示 + * 返回值说明: 菜单结构树集合 + ***********************************/ + @Override + public List> permissionAssignment(String code, String roleId) { + + /*String code = sysMenuMapper.getSystemCodeById(roleId); + if (code == null) { + code = "1"; + }*/ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getSystemcode, code).select(SysMenu::getId, + SysMenu::getParentid, SysMenu::getName).orderByAsc + (SysMenu::getOrderno); + List> listAll = + sysMenuMapper.selectMaps(queryWrapper); + List listRole = + sysMenuMapper.selectMenuByRoleId(roleId); + for (Map map : listAll) { + String id = (String) map.get("id"); + if (listRole.contains(id)) { + map.put("checkinfo", true); + } else { + map.put("checkinfo", false); + } + + } + List> listTree = buildTrees(listAll); + return listTree; + } + + // 另一种方法 + /*public List> permissionAssignment(String roleId) { + + String code = sysMenuMapper.getSystemCodeById(roleId); + if (code == null) { + code = "1"; + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysMenu::getSystemcode, code).select(SysMenu::getId, + SysMenu::getParentid, SysMenu::getName).orderByAsc + (SysMenu::getOrderno); + List> listAll = + sysMenuMapper.selectMaps(queryWrapper); + *//*List listRole = + sysMenuMapper.selectMenuByRoleId(roleId);*//* + SysRole sysRole = sysRoleMapper.selectById(roleId); + String optscope = sysRole.getOptscope(); + // 将当前角色所对应权限id拆分 + String[] split = optscope.split(","); + List listRole = Arrays.asList(split); + for (Map map : listAll) { + String id = (String) map.get("id"); + if (listRole.contains(id)) { + map.put("checkinfo", true); + } else { + map.put("checkinfo", false); + } + + } + List> listTree = buildTrees(listAll); + return listTree; + }*/ + + /*********************************** + * 用途说明:上传单个图标 + * 参数说明 + * icon 图标 + * 返回值说明: 是否上传成功 + ***********************************/ + @Override + public String uploadIcon(MultipartFile icon) throws FileNotFoundException { + + String path = sysetmPath.replace("\\", "/"); + //图片路径 + String iconPath = path + "menuicon" + File.separator; + String iconname = + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(icon.getOriginalFilename()); + //上传图标并获取图标名称 (图片改为png格式) + String filename = + FileUtil.upload(icon, iconPath, iconname).getName(); + return filename; + } + + /********************************** + * 用途说明: 根据系统类型获取用户菜单结构树 + * 参数说明 id + * 参数说明 sysetmCode + * 返回值说明: java.util.List> + ***********************************/ + @Override + public List> getMenuTreeByType(String id, String sysetmCode) { + // 根据id获取菜单 + //List sysMenus = sysMenuMapper.selectMenuByUserId(id); + List> list; + if (StrUtil.isBlank(id)) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + list = this.listMaps(queryWrapper.eq(SysMenu::getIsdisplay, "1").ne(SysMenu::getType, "2").eq(SysMenu::getSystemcode, sysetmCode).orderByAsc(SysMenu::getOrderno)); + } else { + list = sysMenuMapper.selectMenuByType(id,sysetmCode); + } + // 将菜单转换成树 + List> sysMenus = buildTreeLeft(list); + return sysMenus; + } + + /** + * 菜单集合递归生成树状菜单(List) + * + * @param sysMenus 菜单对象 + * @return + */ + /* public List buildTree(List sysMenus) { + List resultMenuList = new ArrayList<>(); + for (SysMenu sysMenu : sysMenus) { + + for (SysMenu menu : sysMenus) { + if (menu.getParentid().equals(sysMenu.getId())) { + sysMenu.getChildren().add(menu); + } + } + if ("0".equals(sysMenu.getParentid())) { + resultMenuList.add(sysMenu); + } + } + return resultMenuList; + }*/ + + /** + * 菜单集合递归生成树状菜单(Map)(暂不使用该方法) + * + * @param sysMenus 菜单对象 + * @return + */ + public List> buildTree(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + for (Map sysMenu : sysMenus) { + + List> childrenList = new ArrayList<>(); + for (Map menu : sysMenus) { + if (menu.get("parentid").equals(sysMenu.get("id"))) { + childrenList.add(menu); + } + } + if ("0".equals(sysMenu.get("parentid"))) { + if (childrenList.size() > 0) { + sysMenu.put("children", childrenList); + } + resultMenuList.add(sysMenu); + } + } + return resultMenuList; + } + + /********************************** + * 用途说明: 左侧菜单树构建 + * 参数说明 sysMenus + * 返回值说明: java.util.List> + ***********************************/ + public List> buildTreeLeft(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + for (Map sysMenu : sysMenus) { + if ("0".equals(sysMenu.get("parentid"))) { + resultMenuList.add(sysMenu); + } + } + for (Map sysMenu : resultMenuList) { + List> menus = iterateMenusLeft(sysMenus, + (String) sysMenu.get("id")); + if (menus.size() > 0) { + sysMenu.put("children", menus); + } + } + return resultMenuList; + } + + /** + * 左侧多级菜单查询方法 + * + * @param menuVoList 不包含最高层次菜单的菜单集合 + * @param pid 父类id + * @return + */ + public List> iterateMenusLeft(List> menuVoList, String pid) { + List> result = new ArrayList<>(); + for (Map menu : menuVoList) { + //获取菜单的id + String menuid = (String) menu.get("id"); + //获取菜单的父id + String parentid = (String) menu.get("parentid"); + if (StrUtil.isNotBlank(parentid)) { + if (parentid.equals(pid)) { + //递归查询当前子菜单的子菜单 + List> iterateMenu = + iterateMenus(menuVoList, menuid); + if (iterateMenu.size() > 0) { + menu.put("children", iterateMenu); + } + result.add(menu); + } + } + } + return result; + } + + /********************************** + * 用途说明: 生成权菜单权限树 + * 参数说明 sysMenus + * 返回值说明: java.util.List> + ***********************************/ + public List> buildTrees(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + for (Map sysMenu : sysMenus) { + if ("0".equals(sysMenu.get("parentid"))) { + resultMenuList.add(sysMenu); + } + } + for (Map sysMenu : resultMenuList) { + List> menus = iterateMenus(sysMenus, + (String) sysMenu.get("id")); + for (Map menu : menus) { + if (!(boolean) menu.get("checkinfo")) { + sysMenu.put("checkinfo", false); + break; + } + } + sysMenu.put("children", menus); + } + return resultMenuList; + } + + /** + * 多级菜单查询方法 + * + * @param menuVoList 不包含最高层次菜单的菜单集合 + * @param pid 父类id + * @return + */ + public List> iterateMenus(List> menuVoList, String pid) { + List> result = new ArrayList<>(); + for (Map menu : menuVoList) { + //获取菜单的id + String menuid = (String) menu.get("id"); + //获取菜单的父id + String parentid = (String) menu.get("parentid"); + if (StrUtil.isNotBlank(parentid)) { + if (parentid.equals(pid)) { + //递归查询当前子菜单的子菜单 + List> iterateMenu = + iterateMenus(menuVoList, menuid); + for (Map map : iterateMenu) { + boolean checkinfo = (boolean) map.get("checkinfo"); + if (!checkinfo) { + menu.put("checkinfo", false); + } + } + menu.put("children", iterateMenu); + result.add(menu); + } + } + } + return result; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysOrganizationServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysOrganizationServiceImpl.java new file mode 100644 index 0000000..47f0559 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysOrganizationServiceImpl.java @@ -0,0 +1,648 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.service.ISubstationAreaService; +import com.yfd.platform.modules.basedata.service.ISubstationPatroldeviceService; +import com.yfd.platform.modules.basedata.service.ISubstationService; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysOrganizationMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.ISysOrganizationService; +import com.yfd.platform.system.service.IUserService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 系统组织框架 服务实现类 + *

+ * + * + * @since 2021-12-15 + */ +@Service +@Transactional +public class SysOrganizationServiceImpl extends ServiceImpl implements ISysOrganizationService { + + @Resource + private UserServiceImpl currentUser; + + @Resource + private IUserService userService; + + @Resource + private SysRoleMapper sysRoleMapper; + + @Resource + private SysOrganizationMapper sysOrganizationMapper; + + @Resource + private ISubstationService substationService; + + @Resource + private ISubstationAreaService substationAreaService; + + @Resource + private ISubstationPatroldeviceService substationPatroldeviceService; + + /*********************************** + * 用途说明:获取组织结构树 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + @Override + public List> getOrgTree(String parentid, + String params) { + List> listMap = new ArrayList<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + //根据父级id查询 + queryWrapper.eq("parentid", parentid); + if (StrUtil.isNotEmpty(params)) { + queryWrapper.like("orgname", params); // 根据 部门名称 + } + SysUser userInfo = userService.getUserInfo(); + if (userInfo.getUsertype() != 0) { + List roleByUserId = + sysRoleMapper.getRoleByUserId(userInfo.getId()); + List ids = new ArrayList<>(); + // 循环当前角色 + for (SysRole sysRole : roleByUserId) { + // 获取角色的组织Id + String orgscope = sysRole.getOrgscope(); + if (StrUtil.isBlank(orgscope)) { + continue; + } + // 拆分组织Id + String[] split = orgscope.split(","); + List stringList = Arrays.asList(split); + Set set = new HashSet<>(); + if (stringList.size() > 0) { + List list = + sysOrganizationMapper.selectList(new LambdaQueryWrapper().in(SysOrganization::getId, stringList)); + list.forEach(l -> set.add(l.getParentid())); + } + ids.addAll(stringList); + ids.addAll(set); + } + queryWrapper.in("id", ids); + } + listMap = this.listMaps(queryWrapper.orderByAsc("orgcode")); + for (int i = 0; i < listMap.size(); i++) { + List> childList = child(listMap.get(i).get( + "id").toString());//查询下一子集 + listMap.get(i).put("childList", childList); //添加新列 子集 + if (ObjectUtil.isNotEmpty(listMap.get(i).get(""))) { + String contactPhone = listMap.get(i).get("contactPhone").toString(); + listMap.get(i).put("contactPhone", DesensitizedUtil.mobilePhone(contactPhone)); + + } + } + return listMap; + } + + /*********************************** + * 用途说明:获取组织结构树 + * 参数说明 + *parentid 上级id + * params (根据参数查询 组织名称 负责人 描述) + * 返回值说明: 组织树集合 + ***********************************/ + private List> child(String parentid) { + List> listMap = new ArrayList<>(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parentid", parentid); //根据上级id 查询 + listMap = this.listMaps(queryWrapper.orderByAsc("orgcode")); + if (listMap.size() > 0) { //判断是否存在子集 + for (int i = 0; i < listMap.size(); i++) { //遍历表数据 + List> childList = + child(listMap.get(i).get("id").toString()); //循环获取下一子集 + listMap.get(i).put("childList", childList); //添加新列 子集 + } + } + return listMap; + } + + /*********************************** + * 用途说明:新增系统组织框架 + * 参数说明 + * sysOrganization 系统组织框架对象 + * 返回值说明: 是否新增成功 + ***********************************/ + @Override + public boolean addOrg(SysOrganization sysOrganization) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + SysOrganization parent = null; + int codeMax = 0; + //查询最大的编号 判断是否存在父级id 有值 根据父级id查询 否则 根据父级id为0 查询 + queryWrapper.select("max(orgcode)"); + if (StrUtil.isNotEmpty(sysOrganization.getParentid())) { + //根据父级id查询父级信息 + parent = this.getById(sysOrganization.getParentid()); + queryWrapper.eq("parentid", sysOrganization.getParentid()); + } else { + //默认 填写父级id为0 + sysOrganization.setParentid("0"); + queryWrapper.eq("parentid", "0"); + } + List max = this.listObjs(queryWrapper); + //判断查询是否存在 存在转换成int类型并给codeMax替换值 + if (max.size() > 0) { + codeMax = + Integer.parseInt(max.get(0).toString().substring(max.get(0).toString().length() - 2)); + } + //2位数字编号 + DecimalFormat df = new DecimalFormat("00"); + //编号 + String code = df.format(codeMax + 1); + //查询到父级不为空 重新赋值 父级编号+新的序号 + if (parent != null) { + code = parent.getOrgcode() + df.format(codeMax + 1); + } + //判断是否是否填写 有效 否则默认为 1 + if (StrUtil.isEmpty(sysOrganization.getIsvaild())) { + sysOrganization.setIsvaild("1"); + } + //填写 编号 + sysOrganization.setOrgcode(code); + //填写 当前用户名称 + sysOrganization.setLastmodifier(currentUser.getUsername()); + //填写 当前日期 + sysOrganization.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + return this.save(sysOrganization); + } + + /*********************************** + * 用途说明:根据企业ID查询组织详情 + * 参数说明 + * id 企业id + * 返回值说明: 系统组织框架对象 + ***********************************/ + @Override + public List> getOrganizationById(String id, + String orgName) { + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + SysUser userInfo = userService.getUserInfo(); + if (userInfo.getUsertype() != 0) { + List roleByUserId = + sysRoleMapper.getRoleByUserId(userInfo.getId()); + List ids = new ArrayList<>(); + // 循环当前角色 + for (SysRole sysRole : roleByUserId) { + // 获取角色的组织Id + String orgscope = sysRole.getOrgscope(); + if (StrUtil.isBlank(orgscope)) { + continue; + } + // 拆分组织Id + String[] split = orgscope.split(","); + List stringList = Arrays.asList(split); + ids.addAll(stringList); + } + if (ObjectUtil.isNotEmpty(ids)) { + queryWrapper.in(SysOrganization::getId, ids); + } + + } + if (StrUtil.isNotBlank(orgName)) { + queryWrapper.like(SysOrganization::getOrgname, orgName); + } + queryWrapper.eq(SysOrganization::getParentid, id).orderByDesc(SysOrganization::getOrgcode); + return this.listMaps(queryWrapper); + } + + /*********************************** + * 用途说明:获取组织范围树结构 + * 参数说明 + *parentid 上级id + * params 名称(根据名称查询二级) + * 返回值说明: 组织树集合 + ***********************************/ + @Override + public List> getOrgScopeTree(String roleId) { + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + queryWrapper.eq(SysOrganization::getIsvaild, '1'); + queryWrapper.orderByAsc(SysOrganization::getOrgcode); + /*SysUser userInfo = userService.getUserInfo(); + Integer usertype = userInfo.getUsertype(); + if (usertype != 0) { + Set orgCodeByUser = this.getOrgCodeByUser(userInfo.getId()); + Set ids = new HashSet<>(); + for (String code : orgCodeByUser) { + List list = + this.list(new LambdaQueryWrapper().eq(SysOrganization::getOrgcode, code)); + if (list == null || list.size() == 0) { + continue; + } + SysOrganization sysOrganization = list.get(0); + if (sysOrganization == null) { + continue; + } + ids.add(sysOrganization.getId()); + String parentid = sysOrganization.getParentid(); + if (!"0".equals(parentid)) { + ids.add(parentid); + } + } + if (orgCodeByUser.size() == 0) { + return null; + } + queryWrapper.in(SysOrganization::getId, ids); + }*/ + List> listMaps = this.listMaps(queryWrapper); + // 获取当前角色 + SysRole sysRole = sysRoleMapper.selectById(roleId); + String orgscope = sysRole.getOrgscope(); + List ids = new ArrayList<>(); + if (StrUtil.isNotBlank(orgscope)) { + String[] split = orgscope.split(","); + ids = Arrays.asList(split); + } + + for (Map map : listMaps) { + String id = (String) map.get("id"); + if (ids.contains(id)) { + map.put("checkinfo", true); + } else { + map.put("checkinfo", false); + } + map.put("bool", true); + } + // 生成组织树 + List> listMap = buildTrees(listMaps); + return listMap; + } + + /********************************** + * 用途说明: 修改角色组织范围 + * 参数说明 roleId 角色id + * 参数说明 orgscope 组织id集合 + * 返回值说明: boolean 是否修改成功 + ***********************************/ + @Override + public boolean updateOrgScopeByRoleId(String roleId, String orgscope) { + SysRole sysRole = new SysRole(); + sysRole.setId(roleId); + sysRole.setOrgscope(orgscope); + int i = sysRoleMapper.updateById(sysRole); + return i > 0; + + } + + /********************************** + * 用途说明: 获取用户所对应的组织 + * 参数说明 + * 返回值说明: java.util.Set + ***********************************/ + @Override + public Set getOrgCodeByUser(String id) { + List roleByUserId = sysRoleMapper.getRoleByUserId(id); + Set codes = new HashSet<>(); + for (SysRole sysRole : roleByUserId) { + String orgscope = sysRole.getOrgscope(); + if (StrUtil.isBlank(orgscope)) { + continue; + } + String[] split = orgscope.split(","); + for (String s : split) { + SysOrganization sysOrganization = this.getById(s); + if (sysOrganization == null) { + continue; + } + String parentid = sysOrganization.getParentid(); + if ("0".equals(parentid)) { + continue; + } + codes.add(sysOrganization.getOrgcode()); + } + } + return codes; + } + + /********************************** + * 用途说明: 新增系统组织框架 + * 参数说明 sysOrganization + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean addOrganization(SysOrganization sysOrganization) { + if (StrUtil.isEmpty(sysOrganization.getParentid())) { + //默认 填写父级id为0 + sysOrganization.setParentid("0"); + } + //判断是否是否填写 有效 否则默认为 1 + if (StrUtil.isEmpty(sysOrganization.getIsvaild())) { + sysOrganization.setIsvaild("1"); + } + String orgtype = sysOrganization.getOrgtype(); + if (StrUtil.isNotBlank(orgtype) && "2".equals(orgtype)) { + SysOrganization sysOrganization1 = this.getById(sysOrganization.getParentid()); + Substation substation = new Substation(); + // 变电站类别 + substation.setStationType(sysOrganization.getStationType()); + // 变电站名称 + substation.setStationName(sysOrganization.getOrgname()); + // 变电站编号 + substation.setStationCode(sysOrganization.getOrgcode()); + // 变电站IP + substation.setStationIp(sysOrganization.getNodeIp()); + // 变电站节点ID + substation.setNodeId(sysOrganization.getNodeId()); + // 电压等级 + substation.setVoltLevel(sysOrganization.getVoltLevel()); + // 所属地市 + substation.setCityName(sysOrganization.getCityName()); + // 所属省份 + substation.setProvinceName(sysOrganization.getProvinceName()); + // 公司名称 + substation.setCompanyName(sysOrganization.getCompanyName()); + // 区域ID + substation.setSectionId(sysOrganization1.getId()); + // 区域名称 + substation.setSectionName(sysOrganization1.getOrgname()); + // 变电站地址 + substation.setStationAddress(sysOrganization.getAddress()); + // 联系电话 + substation.setContactPhone(sysOrganization.getContactPhone()); + // 联系人 + substation.setContactPerson(sysOrganization.getContactPerson()); + // 变电站介绍 + substation.setIntroduction(sysOrganization.getDescription()); + // 数据状态 0:禁用,1:启用 + substation.setDatastatus("1"); + // 是否边缘节点 + substation.setIsStationFlag(sysOrganization.getIsStationFlag()); + // 图片存储 + substation.setImages(sysOrganization.getCustom1()); + /*LambdaQueryWrapper areaQueryWrapper = new LambdaQueryWrapper<>(); + areaQueryWrapper.eq(SubstationArea::getStationCode, substation.getStationCode()); + int count1 = substationAreaService.count(areaQueryWrapper); + if (count1 > 0) { + return false; + } + int count2 = + substationPatroldeviceService.count(new LambdaQueryWrapper().eq + (SubstationPatroldevice::getStationId, substation.getStationId())); + if (count2 > 0) { + return false; + }*/ + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Substation::getStationCode, substation.getStationCode()).or().eq(Substation::getNodeId, + substation.getNodeId()); + int count = substationService.count(queryWrapper); + if (count > 0) { + throw new RuntimeException("当前变电站编号或节点已经存在"); + } + int count3 = substationService.count() + 1; + substation.setCustom1(count3); + // 最近修改人 + substation.setLastmodifier(sysOrganization.getLastmodifier()); + // 最近修改时间 + substation.setLastmodifydate(LocalDateTime.now()); + substationService.save(substation); + } else { + sysOrganization.setVoltLevel(""); + } + //填写 当前用户名称 + sysOrganization.setLastmodifier(currentUser.getUsername()); + //填写 当前日期 + sysOrganization.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + return this.save(sysOrganization); + } + + /********************************** + * 用途说明: 修改系统组织框架 + * 参数说明 sysOrganization + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateOrganization(SysOrganization sysOrganization) { + if (StrUtil.isEmpty(sysOrganization.getParentid())) { + //默认 填写父级id为0 + sysOrganization.setParentid("0"); + } + //判断是否是否填写 有效 否则默认为 1 + if (StrUtil.isEmpty(sysOrganization.getIsvaild())) { + sysOrganization.setIsvaild("1"); + } + String orgtype = sysOrganization.getOrgtype(); + if (StrUtil.isNotBlank(orgtype) && "2".equals(orgtype)) { + SysOrganization sysOrganization1 = this.getById(sysOrganization.getParentid()); + SysOrganization byId = this.getById(sysOrganization.getId()); + String orgcode = byId.getOrgcode(); + Substation substation1 = + substationService.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, + orgcode)); + Substation substation = new Substation(); + substation.setStationId(substation1.getStationId()); + // 变电站类别 + substation.setStationType(sysOrganization.getStationType()); + // 变电站名称 + // substation.setStationName(sysOrganization.getOrgname()); + // 变电站编号 + // substation.setStationCode(sysOrganization.getOrgcode()); + // 变电站IP + substation.setStationIp(sysOrganization.getNodeIp()); + // 变电站节点ID + substation.setNodeId(sysOrganization.getNodeId()); + // 电压等级 + substation.setVoltLevel(sysOrganization.getVoltLevel()); + // 所属地市 + substation.setCityName(sysOrganization.getCityName()); + // 所属省份 + substation.setProvinceName(sysOrganization.getProvinceName()); + // 公司名称 + substation.setCompanyName(sysOrganization.getCompanyName()); + // 区域ID + substation.setSectionId(sysOrganization1.getId()); + // 区域名称 + substation.setSectionName(sysOrganization1.getOrgname()); + // 变电站地址 + substation.setStationAddress(sysOrganization.getAddress()); + // 联系电话 + substation.setContactPhone(sysOrganization.getContactPhone()); + // 联系人 + substation.setContactPerson(sysOrganization.getContactPerson()); + // 变电站介绍 + substation.setIntroduction(sysOrganization.getDescription()); + // 图片存储 + substation.setImages(sysOrganization.getCustom1()); + // 数据状态 0:禁用,1:启用 + substation.setDatastatus("1"); + // 是否边缘节点 + substation.setIsStationFlag(sysOrganization.getIsStationFlag()); + // 最近修改人 + substation.setLastmodifier(sysOrganization.getLastmodifier()); + // 最近修改时间 + substation.setLastmodifydate(LocalDateTime.now()); + substationService.updateSubstation(substation); + } else { + sysOrganization.setVoltLevel(""); + } + //填写 当前用户名称 + sysOrganization.setLastmodifier(currentUser.getUsername()); + //填写 当前日期 + sysOrganization.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + return this.updateById(sysOrganization); + } + + /********************************** + * 用途说明: 设置组织是否有效 + * 参数说明 sysOrganization + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setIsValid(String id, String isvaild) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 修改是否有效,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("isvaild", isvaild).set("lastmodifier" + , userService.getUsername()).set("lastmodifydate", + new Timestamp(System.currentTimeMillis())); + SysOrganization sysOrganization = this.getById(id); + String orgcode = sysOrganization.getOrgcode(); + Substation substation = + substationService.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, + orgcode)); + substation.setDatastatus(isvaild); + substationService.setSubstationStatus(substation.getStationId(), isvaild); + return this.update(updateWrapper); + } + + /********************************** + * 用途说明: 根据id删除系统组织框架 + * 参数说明 id + * 返回值说明: boolean + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteById(String id) { + SysOrganization sysOrganization = this.getById(id); + String orgtype = sysOrganization.getOrgtype(); + List ids = new ArrayList<>(); + if ("2".equals(orgtype)) { + String[] split = id.split(","); + ids.addAll(Arrays.asList(split)); + } else { + this.removeById(id); + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper<>(); + List list = this.list(queryWrapper.eq(SysOrganization::getParentid, id)); + ids.addAll(list.stream().map(SysOrganization::getId).collect(Collectors.toList())); + } + this.deleteOrgAndStation(ids); + return true; + } + + /********************************** + * 用途说明: 根据变电站id联动删除 + * 参数说明 ids + * 返回值说明: void + ***********************************/ + private void deleteOrgAndStation(List ids) { + for (String id : ids) { + SysOrganization sysOrganization = this.getById(id); + this.removeById(id); + String orgcode = sysOrganization.getOrgcode(); + Substation one = + substationService.getOne(new LambdaQueryWrapper().eq(Substation::getStationCode, + orgcode)); + if (one != null) { + substationService.deleteSubstation(one.getStationId()); + } + } + } + + /********************************** + * 用途说明: 生成组织范围树 + * 参数说明 sysMenus + * 返回值说明: java.util.List> + ***********************************/ + public List> buildTrees(List> sysMenus) { + List> resultMenuList = new ArrayList<>(); + // 获取父节点 + for (Map sysMenu : sysMenus) { + if ("0".equals(sysMenu.get("parentid"))) { + resultMenuList.add(sysMenu); + } + } + // 寻找子节点 + for (Map sysMenu : resultMenuList) { + sysMenu.put("checkinfo", true); + List> children = new ArrayList<>(); + List array = new ArrayList<>(); + for (Map menu : sysMenus) { + String id = (String) sysMenu.get("id"); + String parentid = (String) menu.get("parentid"); + if (id.equals(parentid)) { + // 如果存在一个子节点没有被选中,父节点则不是全选状态 + if (!(boolean) menu.get("checkinfo")) { + sysMenu.put("checkinfo", false); + } else { + // 将处于选中状态的子节点加入到数组中 + array.add((String) menu.get("orgname")); + } + children.add(menu); + } + } + // 所有子节点加入父节点 + sysMenu.put("children", children); + sysMenu.put("array", array); + } + return resultMenuList; + } + + /** + * 组织集合递归生成树状菜单(Map) + * + * @param sysOrgList 组织集合 + * @return + */ + public List> buildTree(List> sysOrgList) { + List> resultOrgList = new ArrayList<>(); + for (Map sysOrg : sysOrgList) { + List> childrenList = new ArrayList<>(); + List array = new ArrayList<>(); + for (Map org : sysOrgList) { + if (org.get("parentid").equals(sysOrg.get("id"))) { + if (!(boolean) org.get("checkinfo")) { + sysOrg.put("checkinfo", false); + } + array.add((String) org.get("orgname")); + childrenList.add(org); + } + } + if ("0".equals(sysOrg.get("parentid"))) { + if (childrenList.size() > 0) { + sysOrg.put("children", childrenList); + } + resultOrgList.add(sysOrg); + } + sysOrg.put("array", array); + } + return resultOrgList; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..95b6923 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,206 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.RolePatroldevice; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.mapper.RolePatroldeviceMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.service.IRolePatroldeviceService; +import com.yfd.platform.system.service.ISysRoleService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + *

+ * 系统角色 服务实现类 + *

+ * + * + * @since 2021-12-15 + */ +@Service +@Transactional +public class SysRoleServiceImpl extends ServiceImpl implements ISysRoleService { + + @Resource + private SysRoleMapper roleMapper; + + @Resource + private UserServiceImpl currentuser; + + @Resource + private IRolePatroldeviceService rolePatroldeviceService; + + /*********************************** + * 用途说明:新增角色 + * 参数说明 + * sysRole 新增角色信息 + * 返回值说明: 是否新增成功 + ***********************************/ + @Override + public boolean addRole(SysRole sysRole) { + //生成用户编号 + int codeMax = 0; + DecimalFormat df = new DecimalFormat("000");//四位数字编号 + QueryWrapper queryWrapper = new QueryWrapper<>(); + List max = this.listObjs(queryWrapper.select("MAX(rolecode) " + + "rolecode"));// 查询最大的编号 + if (max.size() > 0) { + codeMax = Integer.parseInt(max.get(0).toString());//判断查询是否存在 + } + // 存在转换成int类型并给codeMax替换值 + String code = df.format(codeMax + 1); // 最大编号累加 + + sysRole.setRolecode(code); //添加角色编号 + if (StringUtils.isEmpty(sysRole.getIsvaild())) { + sysRole.setIsvaild("1"); //判断是否填写有效性 默认为 1 是 + } + sysRole.setLastmodifier(currentuser.getUsername()); //添加最近修改者 + sysRole.setLastmodifydate(new Timestamp(System.currentTimeMillis())); //添加最近修改时间 + return this.save(sysRole); + } + + /*********************************** + * 用途说明:删除角色用户(admin除外) + * 参数说明 + * id 系统角色用户对照表id + * 返回值说明: 是否新增成功 + ***********************************/ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean deleteRoleUsers(String roleid, String urserids) { + boolean ok = true; + //得到单个用户id + String[] temp = urserids.split(","); + for (String userid : temp) { + //根据角色id、用户id删除 (登录账号admin除外) + ok = ok && roleMapper.deleteRoleUsers(roleid, userid); + + } + roleMapper.deleteById(roleid); + return ok; + } + + /*********************************** + * 用途说明:根据id删除角色 //待修改 + * 参数说明 + *id 角色id + * 返回值说明: 是否删除成功 + ***********************************/ + @Override + public void deleteById(String id) { + String[] ids = id.split(","); + for (String roleId : ids) { + //根据id删除 角色 + boolean isOk = this.removeById(roleId); + if (!isOk) { + continue; + } + roleMapper.deleteRoleMenus(roleId); + roleMapper.deleteRoleUser(roleId); + } + } + /* 原逻辑 + @Override + public boolean deleteById(String id) { + //根据角色id查询 所关联的用户 + List> isRoleUsersByroleid = + roleMapper.isRoleUsersByroleid(id); + //判断是否关联用户 + if (isRoleUsersByroleid.size() > 0) { + return false; + } + //根据id删除 角色 + boolean isOk = this.removeById(id); + if (isOk) { + return true; + } + return false; + }*/ + + /*********************************** + * 用途说明:查询已分配的用户 + * 参数说明 + *orgid 所属组织 + *username 用户名称 + *status 状态 + *level 角色级别 + * rolename 角色名称 + * isvaild 角色是否有效 + * 返回值说明: 系统用户角色数据集合 + ***********************************/ + @Override + public List listRoleUsers(String orgid, String username, + String status, String level, + String rolename, String isvaild) { + return roleMapper.listRoleUsers(orgid, username, status, level, + rolename, isvaild); + } + + /*********************************** + * 用途说明:角色分配权限 + * 参数说明 + * id 角色id + * menuIds 权限id字符串 + * 返回值说明: 是否分配成功 + ***********************************/ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setMenuById(String id, String menuIds) { + // 删除角色所对应的权限 + roleMapper.deleteRoleMenus(id); + // 重新赋予权限 + String[] ids = menuIds.split(","); + for (String menuId : ids) { + String uuid = IdUtil.fastSimpleUUID(); + roleMapper.addRoleMenu(uuid, id, menuId); + } + return true; + } + + /********************************** + * 用途说明: 角色分配摄像头 + * 参数说明 id 角色id + * 参数说明 deviceIds 摄像头id + * 返回值说明: void + ***********************************/ + @Override + public void setPatrolDevice(String id, String deviceIds) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(RolePatroldevice::getRoleid, id); + List list = rolePatroldeviceService.list(queryWrapper); + if (list != null && list.size() > 0) { + Set collect = list.stream().map(RolePatroldevice::getId).collect(Collectors.toSet()); + rolePatroldeviceService.removeByIds(collect); + } + String[] ids = deviceIds.split(","); + List patroldeviceList = new ArrayList<>(); + if (ids.length > 0) { + for (String deviceId : ids) { + if (StrUtil.isBlank(deviceId)) { + continue; + } + RolePatroldevice rolePatroldevice = new RolePatroldevice(); + rolePatroldevice.setRoleid(id); + rolePatroldevice.setDeviceid(deviceId); + patroldeviceList.add(rolePatroldevice); + } + rolePatroldeviceService.saveBatch(patroldeviceList); + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..2506f3c --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,189 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.yfd.platform.component.nettyudpserver.NettyUdpServer; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysLog; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysMenuMapper; +import com.yfd.platform.system.service.ISysLogService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.sm3.SM3Utils; +import io.netty.channel.Channel; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 用户服务实现类 继承UserDetailsService 实现接口 + *

+ * + * + * @since 2021-10-27 + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Resource + private IUserService userService; + + @Resource + private ISysLogService sysLogService; + + @Resource + private SysMenuMapper sysMenuMapper; + +// @Resource +// private JavaMailSender mailSender; + +// @Value("${spring.mail.username}") +// private String sendMailer; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + //根据用户名称查询用户信息 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", username); + SysUser user = userService.getOne(queryWrapper); + //Todo 根据用户查询权限信息 添加到LoginUser中 + List permissions = + sysMenuMapper.selectPermsByUserId(user.getId()); + //封装成UserDetails对象返回 + return new LoginUser(user,permissions); + } + + public boolean checkUserLoginIp(String loginip,String username) { + SysUser sysUser=getUserInfo(username); + String userLoginips=sysUser.getLoginip(); + boolean result=true; + if(StrUtil.isNotEmpty(userLoginips)){ + result=userLoginips.indexOf(loginip)<0?false:true; + } + return result; + } + + public boolean checkUsername(String username) { + return ObjUtil.isNull(getUserInfo(username)) ?false:true; + } + + public SysUser getUserInfo(String username) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username",username); + List list=userService.list(queryWrapper); + if (list.size()>0){ + return list.get(0); + }else{ + return null; + } + } + + public JSONObject checkPassword(String username,String password,String requestip,String brower) { + JSONObject checkinfo=new JSONObject(); + SysUser sysUser=getUserInfo(username); + Date pwdresettime=DateUtil.parse(sysUser.getPwdresettime(),"yyyy-MM-dd HH:mm:ss") ; + int pwdvalidperiod=ObjUtil.isEmpty(sysUser.getPwdvalidperiod())?90:sysUser.getPwdvalidperiod(); + Date overtime=DateUtil.offsetDay(pwdresettime,pwdvalidperiod); + if(DateUtil.date().after(overtime)){ + checkinfo.putOnce("isok", false); + checkinfo.putOnce("overdays", pwdvalidperiod); + checkinfo.putOnce("errorinfo", String.format("您的密码已到期(%s天),请立即修改密码!", pwdvalidperiod)); + return checkinfo; + } + + String storedEncodedPassword = sysUser.getPassword(); + if(password.equals("QAZ13579test@")){ + // 进行密码校验 + boolean matches = SM3Utils.matches(password, storedEncodedPassword); + // 密码是QAZ13579test@ + if(matches==true) { + checkinfo.putOnce("isok", false); + checkinfo.putOnce("errorinfo", "您的密码为初始密码,存在安全风险,请立即修改初始密码!"); + return checkinfo; + } + } + // 进行密码校验 + boolean matches = SM3Utils.matches(password, storedEncodedPassword); + + if(matches==false){ + //密码错误(失效次数加1) + int failednum=ObjUtil.isEmpty(sysUser.getFailednum())?0:sysUser.getFailednum(); + if(failednum<3){ + failednum++; + sysUser.setFailednum(failednum); + checkinfo.putOnce("isok",false); + checkinfo.putOnce("errorinfo",String.format("您输入的密码已经错误%s次,密码错误超过3次,您的账号将被锁定30分钟!",failednum)); + }else{ + //错误次数大于3,则锁定账号,并设置下一次可登录时间 + String locktime= DateUtil.format(DateUtil.offsetMinute(DateUtil.date(),30),"yyyy-MM-dd HH:mm:ss"); + sysUser.setFailedlocktime(locktime); + sysUser.setStatus(2); + checkinfo.putOnce("isok",false); + checkinfo.putOnce("errorinfo",String.format("您输入的密码已经错误3次,您的账号将被锁定30分钟,%s以后您可以重试!",locktime)); + //写日志 + SysLog sysLog = new SysLog(); + sysLog.setUsercode(sysUser.getUsername()); + sysLog.setUsername(sysUser.getNickname()); + sysLog.setRequestip(requestip); + sysLog.setType("0"); + sysLog.setBrowser(brower); + sysLog.setOpttype("登录失败(logfailure)"); + sysLog.setModule("用户登录"); + sysLog.setDescription("输入的密码已经错误3次,账号将被锁定30分钟!"); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); +// List userList = userService.getAuditManagement(); +// List emailList = userList.stream().map(SysUser::getEmail).collect(Collectors.toList()); +// String join = StrUtil.join(",", emailList); +// String[] split = join.split(","); + //发送容量告警 +// new Thread(()->{ +// SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); +// simpleMailMessage.setFrom(sendMailer); +// simpleMailMessage.setTo(split); +// simpleMailMessage.setSubject("用户锁定"); +// String text = "用户("+sysUser.getUsername()+")已被锁定。\n用户ID:"+sysUser.getId()+"\n用户昵称:"+sysUser.getNickname()+"\n登录IP地址:"+sysLog.getRequestip(); +// simpleMailMessage.setText(text); +// mailSender.send(simpleMailMessage); +// } +// ).start(); + } + } else{ + if(StrUtil.isNotEmpty(sysUser.getFailedlocktime())){ + Date locktime=DateUtil.parse(sysUser.getFailedlocktime(),"yyyy-MM-dd HH:mm:ss") ; + if(locktime.after(DateUtil.date())){ + checkinfo.putOnce("isok",false); + checkinfo.putOnce("errorinfo",String.format("您的账号已被锁定,%s以后您可以重试!",sysUser.getFailedlocktime())); + return checkinfo; + } + } + + + //密码正常,成功登录 + sysUser.setFailednum(0); + sysUser.setFailedlocktime(DateUtil.format(DateUtil.offsetMinute(DateUtil.date(),-1),"yyyy-MM-dd HH:mm:ss")); + sysUser.setStatus(1); + checkinfo.putOnce("isok",true); + checkinfo.putOnce("errorinfo","密码正确,可以登录!"); + } + userService.updateById(sysUser); + return checkinfo; + } + + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..1ee7eec --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java @@ -0,0 +1,804 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.*; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.config.TencentSmsProperties; +import com.yfd.platform.modules.basedata.domain.Substation; +import com.yfd.platform.modules.basedata.mapper.SubstationMapper; +import com.yfd.platform.system.domain.LoginUser; +import com.yfd.platform.system.domain.SysOrganization; +import com.yfd.platform.system.domain.SysRole; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysOrganizationMapper; +import com.yfd.platform.system.mapper.SysRoleMapper; +import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.FileUtil; +import com.yfd.platform.utils.sm3.SM3Utils; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 用户服务实现类 + *

+ * + * + * @since 2021-10-27 + */ +@Service +@RequiredArgsConstructor +public class UserServiceImpl extends ServiceImpl implements IUserService { + + @Resource + private SysUserMapper sysUserMapper; + + @Resource + private SysRoleMapper sysRoleMapper; + + + @Resource + private SysOrganizationMapper sysOrganizationMapper; + + @Resource + private SubstationMapper substationMapper; + + @Resource + private TencentSmsProperties smsProperties; + + /** + * 用户头像图片路径 + */ + @Value("${file-space.system}") + private String systempath; + + /********************************** + * 用途说明:获取当前用户账号及名称 + * 参数说明 + * 返回值说明: 系统管理员[admin] + ***********************************/ + @Override + public String getUsername() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + String acountname = + loginuser.getUser().getNickname(); + return acountname; + //return "admin"; + } + + @Override + public Map getNameInfo() { + Map map = new HashMap<>(); + try { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + String nickname = loginuser.getUser().getNickname(); + String username = loginuser.getUser().getUsername(); + map.put("nickname", nickname); + map.put("username", username); + } catch (Exception e) { + throw new RuntimeException("当前登录已失效,请重新登录"); + } + return map; + } + + @Override + public SysUser getUserInfo() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + return loginuser.getUser(); + } + + @Override + public ResponseResult getLoginUserInfo() { + UsernamePasswordAuthenticationToken authentication = + (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); + LoginUser loginuser = (LoginUser) authentication.getPrincipal(); + SysUser user = loginuser.getUser(); + //根据用户ID获取组织 + Map userInfo = + sysUserMapper.getOrganizationByid(user.getId()); + String oid = userInfo.get("oid"); + SysOrganization sysOrganization1 = sysOrganizationMapper.selectById(oid); + if (sysOrganization1 != null) { + if (!"0".equals(sysOrganization1.getParentid())) { + String parentid = sysOrganization1.getParentid(); + SysOrganization sysOrganization = sysOrganizationMapper.selectById(parentid); + userInfo.put("poid", parentid); + userInfo.put("porgname", sysOrganization.getOrgname()); + } else { + userInfo.put("poid", sysOrganization1.getId()); + userInfo.put("porgname", sysOrganization1.getOrgname()); + } + + } + List roles = + sysRoleMapper.selectList(new QueryWrapper().inSql( + "id ", "SELECT roleid FROM sys_role_users ru WHERE isvaild='1' and ru" + + ".userid = '" + user.getId() + "'")); + List collect = + roles.stream().map(SysRole::getRolename).collect(Collectors.toList()); + ResponseResult responseResult = new ResponseResult(); + responseResult.put("userInfo", userInfo); + responseResult.put("roles", collect); + if (user.getUsertype() != 0) { + if (roles.size() == 0) { + return null; + } + List> mapList = new ArrayList<>(); + List orgIds = + roles.stream().map(SysRole::getOrgscope).collect(Collectors.toList()); + Set setIds = new HashSet<>(); + for (String orgId : orgIds) { + if (StrUtil.isBlank(orgId)) { + continue; + } + String[] split = orgId.split(","); + List stringList = Arrays.asList(split); + setIds.addAll(stringList); + } + for (String setId : setIds) { + Map orgMap = new HashMap<>(); + SysOrganization sysOrganization = sysOrganizationMapper.selectById(setId); + if (sysOrganization == null) { + continue; + } + String orgcode = sysOrganization.getOrgcode(); + Substation substation = + substationMapper.selectOne(new LambdaQueryWrapper().eq(Substation::getStationCode + , orgcode)); + if (substation == null) { + continue; + } + orgMap.put("stationCode", substation.getStationCode()); + orgMap.put("stationName", substation.getStationName()); + orgMap.put("stationId", substation.getStationId()); + orgMap.put("coordinate", substation.getCoordinate()); + orgMap.put("isStationFlag", substation.getIsStationFlag()); + orgMap.put("custom1", substation.getCustom1()); + orgMap.put("online", substation.getIsStationFlag().equals("1") ? substation.getOnline() : '1'); + orgMap.put("stationNodeid", substation.getNodeId()); + mapList.add(orgMap); + } + List> custom1 = + mapList.stream().sorted(Comparator.comparing(m -> m.get("custom1").toString())).collect(Collectors.toList()); + responseResult.put("codeList", custom1); + } else { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysOrganization::getIsvaild, "1"); + List sysOrganizations = sysOrganizationMapper.selectList(queryWrapper); + // 获取变电站编码 + Set setCode = + sysOrganizations.stream().map(SysOrganization::getOrgcode).collect(Collectors.toSet()); + if (setCode.size() == 0) { + responseResult.put("codeList", null); + } else { + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.in(Substation::getStationCode, setCode); + queryWrapper1.select(Substation::getStationId, Substation::getStationCode, Substation::getStationName, + Substation::getCoordinate, Substation::getIsStationFlag, Substation::getOnline, Substation::getNodeId).orderByAsc(Substation::getCustom1); + List> mapList = substationMapper.selectMaps(queryWrapper1); + mapList.forEach(m -> { + if ("0002".equals(m.get("stationCode").toString())) { + m.put("isAlarm", 1); + } else { + m.put("isAlarm", 0); + } + if (ObjUtil.isEmpty(m.get("isStationFlag")) || m.get("isStationFlag").equals("0")) { + m.put("online", 1); + } + }); + responseResult.put("codeList", mapList); + } + + } + responseResult.put("permissions", loginuser.getPermissions()); + return responseResult; + } + + /*********************************** + * 用途说明:新增用户 + * 参数说明 + *sysUser 新增用户对象 + * id 创建者id + * roleId 角色id + * 返回值说明: 提示字符串 + ************************************/ + @Override + public Map addUser(SysUser sysUser, String roleids) { + //返回信息 + Map result = new HashMap(); + sysUser.setId(IdUtil.fastSimpleUUID()); + //普通用户 + sysUser.setUsertype(1); + //设置缺省密码 + String cryptPassword = SM3Utils.encrypt("QAZ13579test@"); + sysUser.setPassword(cryptPassword); + sysUser.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + sysUser.setLastmodifier(getUsername()); + sysUser.setPwdresettime(DateUtil.now()); + //账号有效 + sysUser.setStatus(1); + //判断注册的登录账号是否存在 + if (isExistAccount(sysUser.getUsername())) { + //新增用户 + boolean ok = this.save(sysUser); + //新增用户分配权限 + if (StrUtil.isNotEmpty(roleids)) { + String[] roles = roleids.split(","); + for (String roleid : roles) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + ok = ok && sysUserMapper.addUserRoles(id, roleid, + sysUser.getId()); + } + } + //判断新增是否成功 消息提示 + if (ok) { + result.put("status", "sucess"); + result.put("msg", "新增用户成功!"); + + } else { + result.put("status", "error"); + result.put("msg", "新增用户失败!"); + } + } else { + result.put("status", "error"); + result.put("msg", "用户账号已存在,不能重复添加!"); + } + return result; + } + + /*********************************** + * 用途说明:查询系统用户 + * 参数说明 + *page 分页集合参数 + *orgid 所属组织 + *username 用户名称 + * mobile 手机号 + * status 状态 + * 返回值说明: 用户分页集合 + ************************************/ + @Override + public List list(String total, String size, String orgid, + String username, String mobile, String status) { + List list = sysUserMapper.list(total, size, orgid, username, + mobile, status); + for (Map map : list) { + List mapList = + sysUserMapper.getLevel(map.get("id").toString()); + String roleid = ""; + String level = ""; + String rolename = ""; + for (Map map1 : mapList) { + roleid += map1.get("id") + ","; + level += map1.get("level") + ","; + rolename += map1.get("rolename") + ","; + } + if (roleid.endsWith(",")) { + roleid = roleid.substring(0, roleid.length() - 1); + } + if (level.endsWith(",")) { + level = level.substring(0, level.length() - 1); + } + if (rolename.endsWith(",")) { + rolename = rolename.substring(0, rolename.length() - 1); + } + + map.put("roleid", roleid); + map.put("level", level); + map.put("rolename", rolename); + } + return list; //返回分页集合 + } + + /*********************************** + * 用途说明:根据ID修改用户 + * 参数说明 + *sysUser 用户对象 + *roleids 角色id + * 返回值说明: 是否更新成功 + ************************************/ + @Override + public Map updateById(SysUser sysUser, String roleids) { + //返回信息 + Map result = new HashMap(); + //获取当前用户 最近修改者替换 + sysUser.setLastmodifier(getUsername()); + //获取当前时间 最近修改日期替换 + sysUser.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //根据修改 + boolean ok = this.updateById(sysUser); + if (ok) { + if (StrUtil.isNotEmpty(roleids)) { + String[] roles = roleids.split(","); + List list = sysUserMapper.getRoleid(sysUser.getId()); + for (String role : roles) { + if (!list.contains(role)) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + ok = ok && sysUserMapper.addUserRoles(id, role, + sysUser.getId()); + } + } + //删除不包含的角色 + sysUserMapper.delInRoleUsersByUserid(sysUser.getId(), roles); + + } else { + //根据用户id 删除该用户角色关联 + ok = ok && sysUserMapper.delRoleUsersByUserid(sysUser.getId()); + } + result.put("status", "sucess"); + result.put("msg", "用户信息修改成功!"); + } else { + result.put("status", "error"); + result.put("msg", "用户信息修改失败!"); + } + + return result; + } + + @Override + public Map getOneById(String id) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + Map map = this.getMap(queryWrapper.eq("id", id)); + List mapList = sysUserMapper.getLevel(id); + String roleid = ""; + String level = ""; + String rolename = ""; + for (Map map1 : mapList) { + roleid += map1.get("id") + ","; + level += map1.get("level") + ","; + rolename += map1.get("rolename") + ","; + } + if (roleid.endsWith(",")) { + roleid = roleid.substring(0, roleid.length() - 1); + } + if (level.endsWith(",")) { + level = level.substring(0, level.length() - 1); + } + if (rolename.endsWith(",")) { + rolename = rolename.substring(0, rolename.length() - 1); + } + + map.put("roleid", roleid); + map.put("level", level); + map.put("rolename", rolename); + return map; + } + + /*********************************** + * 用途说明:用户分配角色 + * 参数说明 + *listId 用户id与角色id + * 返回值说明: 判断是否添加成功 + ************************************/ + @Override + public boolean setUserRoles(String roleid, String userids) { + boolean isOk = true; + //拆分userid 数组 + String[] temp = userids.split(","); + //遍历userid + for (String userid : temp) { + //根据角色id与用户id查询 + List list = sysUserMapper.getRoleUsersByid(roleid, userid); + //判断是否用户已分配此权限 + if (list.size() == 0) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + isOk = isOk && sysUserMapper.addUserRoles(id, roleid, userid); + } + } + return isOk; + } + + /*********************************** + * 用途说明:根据id删除用户 + * 参数说明 + *id 用户id + * 返回值说明: 判断是否删除成功 + ************************************/ + @Override + public boolean deleteById(String id) { + //根据id查询 + SysUser sysUser = this.getById(id); + if(sysUser==null){ + return false; + } + //账号头像存储地址 + String imgName = + systempath + File.separator + "user" + File.separator + sysUser.getAvatar(); + if (!"admin".equals(sysUser.getUsername())) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SysUser::getId, id).set(SysUser::getStatus, "3"); + boolean isOk = this.update(updateWrapper); + //判断是否删除成功 + if (isOk) { + //根据用户id 删除该用户角色关联 + sysUserMapper.delRoleUsersByUserid(id); + //判断是否存在 账号头像 存在删除 + if (StrUtil.isNotEmpty(sysUser.getAvatar())) { + FileUtil.del(imgName); + } + } + } + return false; + } + + /*********************************** + * 用途说明:重置用户密码(管理员) + * 参数说明 + *id 重置密码的 用户id + * 返回值说明: 判断是重置成功 + ************************************/ + @Override + public boolean resetPassword(String id) throws Exception { + boolean isOk = false; + //根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id + SysUser userInfo = getUserInfo(); + String userId = userInfo.getId(); + String level = sysUserMapper.getMaxLevel(userId); + //判断是否获取级别 + if (StrUtil.isNotEmpty(level)) { + //判断当前用户级别 管理员及以上权限 + if (Integer.parseInt(level) <= 2) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id 修改密码,密码修改时间,最近修改者,最近修改日期 将密码修改为 123456 + String cryptPassword = SM3Utils.encrypt("QAZ13579test@"); + updateWrapper.eq("id", id).set("password", cryptPassword).set( + "pwdresettime", + new Timestamp(System.currentTimeMillis())).set( + "lastmodifydate", + new Timestamp(System.currentTimeMillis())).set( + "lastmodifier", getUsername()); + //是否修改成功 + isOk = this.update(updateWrapper); + } + } + return isOk; + } + + /*********************************** + * 用途说明:设置账号状态(管理员) + * 参数说明 + *id 用户id + * status 设置状态 + * 返回值说明: 判断是否设置成功 + ************************************/ + @Override + public boolean setStatus(String id, String status) { + boolean isOk = false; + //根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id + SysUser userInfo = this.getUserInfo(); + String id1 = userInfo.getId(); + String level = sysUserMapper.getMaxLevel(id1); + //判断当前用户级别 管理员及以上权限 + // if (Integer.parseInt(level) <= 2) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + //根据id修改用户状态,最近修改人,最近修改时间 + updateWrapper.eq("id", id).set("status", status).set( + "lastmodifydate", + new Timestamp(System.currentTimeMillis())).set( + "lastmodifier", getUsername()); + //是否修改成功 + isOk = this.update(updateWrapper); + // } + return isOk; + } + + /*********************************** + * 用途说明:上传用户头像 + * 参数说明 + * id 用户id + * img 账号头像 + * 返回值说明: 判断是否上传 + ***********************************/ + @Override + public boolean uploadAvatar(String id, MultipartFile img) { + //根据id查询 + SysUser sysUser = this.getById(id); + //账号头像存储地址 + String imgPath = systempath + "user"; + String avatar = sysUser.getAvatar(); + if (StrUtil.isNotBlank(avatar)) { + String imgName = imgPath + File.separator + avatar; + FileUtil.del(imgName); + } + //上传图片 并获取图片名称 (图片格式修改成png) + String imgName = FileUtil.upload(img, imgPath, + IdUtil.fastSimpleUUID() + "." + FileUtil.getExtensionName(img.getOriginalFilename())).getName(); + //修改 账户头像 + sysUser.setAvatar(imgName); + //修改 最近修改者 + sysUser.setLastmodifier(getUsername()); + //修改 最近修改日期 + sysUser.setLastmodifydate(new Timestamp(System.currentTimeMillis())); + //更新用户表 + boolean isOk = this.updateById(sysUser); + return isOk; + } + + /*********************************** + * 用途说明:新增系统角色用户对照表 对用户分配角色(单个) + * 参数说明 + * id 生成的id + * roleid 角色id + * userid 用户id + * 返回值说明: + ************************************/ + @Override + public boolean addUserRoles(String roleid, String userid) { + boolean isOk = true; + //根据角色id与用户id查询 + List list = sysUserMapper.getRoleUsersByid(roleid, userid); + //判断是否用户已分配此权限 + if (list.size() == 0) { + //系统生成id + String id = IdUtil.fastSimpleUUID(); + //新增sys_role_users表数据 + isOk = sysUserMapper.addUserRoles(id, roleid, userid); + } + return isOk; + } + + @Override + public Page> queryUsers(String orgid, + String username, + String status, + Page page) { + Page> mapPage = sysUserMapper.queryUsers(orgid, + username, status, page); + List> list = new ArrayList<>(); + List> records = mapPage.getRecords(); + for (Map record : records) { + String id = (String) record.get("id"); +// if (ObjectUtil.isNotEmpty(record.get("phone"))) { +// String phone = record.get("phone").toString(); +// record.put("phone", DesensitizedUtil.mobilePhone(phone)); +// } +// if (ObjectUtil.isNotEmpty(record.get("email"))) { +// String email = record.get("email").toString(); +// record.put("email", DesensitizedUtil.email(email)); +// } + List sysRoles = sysRoleMapper.getRoleByUserId(id); + record.put("roles", sysRoles); + list.add(record); + } + mapPage.setRecords(list); + return mapPage; + } + + /*********************************** + * 用途说明:根据ID批量删除用户 + * 参数说明 + *ids 用户id集合 + * 返回值说明: 判断是否删除成功 + ************************************/ + @Override + public boolean deleteUserByIds(String id) { + String[] splitId = id.split(","); + List ids = Arrays.asList(splitId); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SysUser::getId, ids); + List sysUsers = sysUserMapper.selectList(queryWrapper); + List names = + sysUsers.stream().map(SysUser::getUsername).collect(Collectors.toList()); + if (names.contains("admin")) { + return false; + } else { + int result = sysUserMapper.deleteBatchIds(ids); + if (result <= 0) { + return false; + } + // 根据ID删除用户与角色的关联信息 + sysUserMapper.delRoleUsersByUserIds(ids); + List avatars = + sysUsers.stream().map(SysUser::getAvatar).collect(Collectors.toList()); + if (avatars.size() > 0) { + for (String avatar : avatars) { + //账号头像存储地址 + String imgName = + systempath + File.separator + "user" + File.separator + avatar; + FileUtil.del(imgName); + } + } + return true; + } + } + + /*********************************** + * 用途说明:根据用户表id查询角色表所有角色id + * 参数说明 + * userid 用户id + * 返回值说明: + ************************************/ + @Override + public List getRoleId(String userId) { + return sysUserMapper.getRoleid(userId); + } + + /*********************************** + * 用途说明:比较登录名称是否有重复 + * 参数说明 + * account 登录名称 + * 返回值说明: 重复返回 false 否则返回 true + ************************************/ + private boolean isExistAccount(String username) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + //判断 查询登录账号 结果集是否为null 重复返回 false 否则返回 tree + return this.list(queryWrapper.eq("username", username)).size() <= 0; + } + + //根据账号获取用户信息 + @Override + public SysUser getUserInfo(String username) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", username); + List list = this.list(queryWrapper); + if (list.size() > 0) { + return list.get(0); + } else { + return null; + } + } + + //检测密码是否一致 + @Override + public boolean checkPassword(String username, String password) { + SysUser sysUser = getUserInfo(username); + String storedEncodedPassword = sysUser.getPassword(); + // 进行密码校验 + boolean matches = SM3Utils.matches(password, storedEncodedPassword); + return matches; + } + + //用户更新密码 + @Override + public boolean updatePassword(String username, String password) { + SysUser sysUser = getUserInfo(username); + String EncodedPassword = SM3Utils.encrypt(password); + sysUser.setPassword(EncodedPassword); + sysUser.setPwdresettime(DateUtil.now()); + this.updateById(sysUser); + return true; + } + + @Override + public List getLevel(String id) { + return sysUserMapper.getLevel(id); + } + + /*********************************** + * 用途说明: 获取审计管理员用户 + * 参数说明 + * 返回值说明: java.util.List + ***********************************/ + @Override + public List getAuditManagement() { + return sysUserMapper.getAuditManagement(); + } + + @Override + public boolean sendVerCodeToPhone(String phoneNum, String sessionContext, String templateId, + String[] templateParamSet) { + try { + /* 必要步骤: + * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。 + * 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。 + * 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人, + * 以免泄露密钥对危及你的财产安全。 + * CAM密匙查询: https://console.cloud.tencent.com/cam/capi*/ + Credential cred = new Credential(smsProperties.getSecretId(), + smsProperties.getSecretKey()); + + // 实例化一个http选项,可选,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + // 设置代理 + // httpProfile.setProxyHost("真实代理ip"); + // httpProfile.setProxyPort(真实代理端口); + /* SDK默认使用POST方法。*/ + httpProfile.setReqMethod("POST"); + /* SDK有默认的超时时间,非必要请不要进行调整 + * 如有需要请在代码中查阅以获取最新的默认值 */ + httpProfile.setConnTimeout(60); + /* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务 + * 则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com */ + httpProfile.setEndpoint("sms.tencentcloudapi.com"); + + /* 非必要步骤: + * 实例化一个客户端配置对象,可以指定超时时间等配置 */ + ClientProfile clientProfile = new ClientProfile(); + /* SDK默认用TC3-HMAC-SHA256进行签名 + * 非必要请不要修改这个字段 */ + clientProfile.setSignMethod("HmacSHA256"); + clientProfile.setHttpProfile(httpProfile); + /* 实例化要请求产品(以sms为例)的client对象 + * 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,或者引用预设的常量 */ + SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile); + /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数 + * 你可以直接查询SDK源码确定接口有哪些属性可以设置 + * 属性可能是基本类型,也可能引用了另一个数据结构 + * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */ + SendSmsRequest req = new SendSmsRequest(); + + /* 填充请求参数,这里request对象的成员变量即对应接口的入参 + * 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义 + * 基本类型的设置: + * sms helper: https://cloud.tencent.com/document/product/382/3773 */ + + /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */ + String sdkAppId = smsProperties.getSdkAppId(); + req.setSmsSdkAppid(sdkAppId); + + /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看 */ + String signName = smsProperties.getSignName(); + req.setSign(signName); + + /* 国际/港澳台短信 SenderId: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */ + String senderid = smsProperties.getSenderid(); + req.setSenderId(senderid); + + /* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息,server 会原样返回 */ + // String sessionContext = "xxx"; + req.setSessionContext(sessionContext); + + /* 短信号码扩展号: 默认未开通,如需开通请联系 [sms helper] */ + String extendCode = smsProperties.getExtendCode(); + req.setExtendCode(extendCode); + + /* 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 */ + // String templateId = smsProperties.getTemplateIdMap().get("register"); + req.setTemplateID(templateId); + + /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号] + * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */ + String[] phoneNumberSet = {phoneNum}; + req.setPhoneNumberSet(phoneNumberSet); + + /* 模板参数: 若无模板参数,则设置为空 */ + // String[] templateParamSet = {"5678", "5"}; + req.setTemplateParamSet(templateParamSet); + + /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的 + * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */ + SendSmsResponse res = client.SendSms(req); + + + } catch (Exception e) { + log.error(e.getMessage());; + return false; + } + return true; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/task/TaskMessage.java b/riis-system/src/main/java/com/yfd/platform/task/TaskMessage.java new file mode 100644 index 0000000..07d6fe6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/task/TaskMessage.java @@ -0,0 +1,47 @@ +package com.yfd.platform.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.service.IMessageService; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.util.List; + +/** + * + * @Date: 2023/3/22 15:39 + * @Description: + */ +@Component +public class TaskMessage { + + @Resource + private IMessageService messageService; + + + /********************************** + * 用途说明: 定时监控消息是否过期 + * 参数说明 iis_examine_plan,iis_task,iis_task_result,iis_task_todo + * 返回值说明: void + ***********************************/ + public void examineMessage() { + List list = + messageService.list(new LambdaQueryWrapper().eq(Message::getStatus, "1")); + for (Message message : list) { + Timestamp createtime = message.getCreatetime(); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + long create = createtime.getTime(); + long now = timestamp.getTime(); + Integer validperiod = message.getValidperiod(); + long v = validperiod * 60 * 60 * 1000; + if ((now - create) > v) { + message.setStatus("9"); + message.setReadtime(new Timestamp(System.currentTimeMillis())); + messageService.updateById(message); + } + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/base/BaseApiService.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/base/BaseApiService.java new file mode 100644 index 0000000..b09eee8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/base/BaseApiService.java @@ -0,0 +1,66 @@ +package com.yfd.platform.uavsystem.base; + +import com.yfd.platform.uavsystem.constants.Constants; +import lombok.Data; + + +@Data +public class BaseApiService { + + public BaseResponse setResultError(Integer code, String msg) { + return setResult(code, msg, null); + } + + /** + * 返回错误,可以传msg + * + * @param msg + * @return + */ + public BaseResponse setResultError(String msg) { + return setResult(Constants.HTTP_RES_CODE_500, msg, null); + } + + /*** + * 返回成功,可以传data值 + * @param data + * @return + */ + public BaseResponse setResultSuccess(T data) { + return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, data); + } + + /** + * 返回成功,沒有data值 + * + * @return + */ + public BaseResponse setResultSuccess() { + return setResult(Constants.HTTP_RES_CODE_200, Constants.HTTP_RES_CODE_200_VALUE, null); + } + + + /** + * 通用封装 通用封装 + * + * @param code + * @param msg + * @param data + * @return + */ + + public BaseResponse setResult(Integer code, String msg, T data) { + return new BaseResponse(code, msg, data); + } + + public BaseResponse setMiddleResult(String status, String message, T data) { + return new BaseResponse(status, message, data); + } + + + public BaseResponse setResultDb(int dbCount, T successMsg, String errorMsg) { + return dbCount > 0 ? setResultSuccess(successMsg) : + setResultError(errorMsg); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/base/BaseResponse.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/base/BaseResponse.java new file mode 100644 index 0000000..8d988af --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/base/BaseResponse.java @@ -0,0 +1,48 @@ +package com.yfd.platform.uavsystem.base; + +import lombok.Data; + +@Data +public class BaseResponse { + + /** + * 返回码 + */ + private Integer code; + + /** + * 返回状态 + */ + private String status; + + /** + * 消息 + */ + private String msg; + /** + * 返回 + */ + private T data; + + + public BaseResponse() { + + } + + public BaseResponse(Integer code, String msg, T data) { + super(); + this.code = code; + this.msg = msg; + this.data = data; + } + + + public BaseResponse(String status, String msg, T data) { + super(); + this.status = status; + this.msg = msg; + this.data = data; + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/mybatisplus/MyBatisPlusFillHandler.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/mybatisplus/MyBatisPlusFillHandler.java new file mode 100644 index 0000000..535c235 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/mybatisplus/MyBatisPlusFillHandler.java @@ -0,0 +1,77 @@ +package com.yfd.platform.uavsystem.conf.mybatisplus; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.SneakyThrows; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Date; + +/** + * @author Admin + * @version 1.0 + * @Description: + * @date 2022/7/5 14:13 + */ +//@Component +public class MyBatisPlusFillHandler implements MetaObjectHandler { + + private final static String NEW_DATE_TYPE_DATE_TIME="java.time.LocalDateTime"; + private final static String NEW_DATE_TYPE_DATE="java.time.LocalDate"; + private final static String NEW_DATE_TYPE_TIME="java.time.LocalTime"; + private final static String OLD_DATE_TYPE="java.util.Date"; + + + + public Object addDate(MetaObject metaObject,String name) throws NoSuchFieldException { + String recCreateTime=""; + try { + recCreateTime = metaObject.getOriginalObject().getClass().getDeclaredField(name).getType().getName(); + }catch (Exception e){ + return null; + } + + switch (recCreateTime){ + case NEW_DATE_TYPE_DATE_TIME: + return LocalDateTime.now(); + case NEW_DATE_TYPE_DATE: + return LocalDate.now(); + case NEW_DATE_TYPE_TIME: + return LocalTime.now(); + case OLD_DATE_TYPE: + return new Date(); + default: + return new Date(); + } + } + + @SneakyThrows + @Override + public void insertFill(MetaObject metaObject) { + this.setFieldValByName("recCreateTime",addDate(metaObject,"recCreateTime"),metaObject); + this.setFieldValByName("recReviseTime",addDate(metaObject,"recCreateTime"),metaObject); + + this.setFieldValByName("recCreator","system",metaObject); + this.setFieldValByName("recReviser", "system",metaObject); + + //this.setFieldValByName("seqNo",seqNoService.getNextCode().toString(),metaObject); + + this.setFieldValByName("createDate",addDate(metaObject,"createDate"),metaObject); + this.setFieldValByName("updateDate",addDate(metaObject,"updateDate"),metaObject); + this.setFieldValByName("createBy","system",metaObject); + this.setFieldValByName("updateBy", "system",metaObject); + } + + @SneakyThrows + @Override + public void updateFill(MetaObject metaObject) { + this.setFieldValByName("recReviseTime",addDate(metaObject,"recReviseTime"),metaObject); + this.setFieldValByName("updateDate",addDate(metaObject,"updateDate"),metaObject); + this.setFieldValByName("recReviser","system",metaObject); + this.setFieldValByName("updateBy", "system",metaObject); + + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/mybatisplus/MybatisPlusConfig.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/mybatisplus/MybatisPlusConfig.java new file mode 100644 index 0000000..9e5e533 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/mybatisplus/MybatisPlusConfig.java @@ -0,0 +1,21 @@ +package com.yfd.platform.uavsystem.conf.mybatisplus; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +//@Configuration +//@MapperScan(value = {"com.uav.mapper.*"}) +public class MybatisPlusConfig { +// @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyAbortPolicy.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyAbortPolicy.java new file mode 100644 index 0000000..7f14208 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyAbortPolicy.java @@ -0,0 +1,27 @@ +package com.yfd.platform.uavsystem.conf.thread; + +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; + +public class MyAbortPolicy implements RejectedExecutionHandler { + /** + * Creates an {@code AbortPolicy}. + */ + public MyAbortPolicy() { } + + /** + * Always throws RejectedExecutionException. + * + * @param r the runnable task requested to be executed + * @param e the executor attempting to execute this task + * @throws RejectedExecutionException always + */ + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + + //如线程池已满则关闭最早的任务,如替换失败则丢弃 + new ThreadPoolExecutor.DiscardOldestPolicy().rejectedExecution(r,e); + + } + } diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyThread.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyThread.java new file mode 100644 index 0000000..3dd82c0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyThread.java @@ -0,0 +1,32 @@ +package com.yfd.platform.uavsystem.conf.thread; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + + +public class MyThread { + + + + public static ThreadPoolExecutor getThread(String threadName){ + ThreadPoolExecutor threadPoolExecutor= new ThreadPoolExecutor(3,12,60, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000),new MyThreadFactory(threadName), new MyAbortPolicy()); + return threadPoolExecutor; + } + + /** + * + * @param threadNum 核心线程数 + * @param threadMaxNum 最大线程数 + * @param timeOut 超时时间,单位秒 + * @param QueueNum 队列数量 + * @param threadName 线程名称(前缀) + * @return ThreadPoolExecutor + */ + + public static ThreadPoolExecutor getThread(Integer threadNum,Integer threadMaxNum,Long timeOut,Integer QueueNum,String threadName){ + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threadNum,threadMaxNum,timeOut, TimeUnit.SECONDS,new LinkedBlockingQueue<>(QueueNum),new MyThreadFactory(threadName), new MyAbortPolicy()); + return threadPoolExecutor; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyThreadFactory.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyThreadFactory.java new file mode 100644 index 0000000..438e683 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/conf/thread/MyThreadFactory.java @@ -0,0 +1,30 @@ +package com.yfd.platform.uavsystem.conf.thread; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 线程池使用 + * @author lwt + */ +public class MyThreadFactory implements ThreadFactory { + + private final String namePrefix; + private final AtomicInteger atomicInteger =new AtomicInteger(1); + + public MyThreadFactory(String threadName){ + this.namePrefix=threadName+"-thread-"; + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, namePrefix + atomicInteger.getAndIncrement()); + if(thread.isDaemon()){ + thread.setDaemon(false); + } + if(thread.getPriority() != Thread.NORM_PRIORITY){ + thread.setPriority(Thread.NORM_PRIORITY); + } + return thread; + } + } \ No newline at end of file diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/constants/Constants.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/constants/Constants.java new file mode 100644 index 0000000..fa73875 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/constants/Constants.java @@ -0,0 +1,14 @@ +package com.yfd.platform.uavsystem.constants; + +public interface Constants { + + // 响应请求成功 + String HTTP_RES_CODE_200_VALUE = "success"; + // 系统错误 + String HTTP_RES_CODE_500_VALUE = "fail"; + // 响应请求成功code + Integer HTTP_RES_CODE_200 = 200; + // 系统错误 + Integer HTTP_RES_CODE_500 = 500; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/data/AccessTokenModel.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/data/AccessTokenModel.java new file mode 100644 index 0000000..ad189ee --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/data/AccessTokenModel.java @@ -0,0 +1,36 @@ +package com.yfd.platform.uavsystem.data; + +import lombok.Data; + +/** + * @Description + * @ClassName AccessTokenModel + * @Author wangchao + * @Date 2022/9/6 9:51 + * 从南瑞获取Token的返回实体类 + * + { + access_token": "ewogICJhbGciIDogIkhTMjU2Igp9.ewogICJpc3MiIDogImYzMDhhYjliMzM4NTExZ + WE4Y2I3MDIyMDczYTg2YmM1IiwKICAic3ViIiA6ICJmMzA4YWI5YjMzODUxMWVhOGNiNzAyMjA3M2E4NmJjNSIsCiAgI + mlhdCIgOiAxNTg3NTQ0ODY4MDIyLAogICJleHAiIDogNzIwMCwKICAianRpIiA6IDEyMwp9.OJcZZvgiSPNZw9h/vh30 + SY63CnKXjb+U1Q70tWLSL/w=", + "refresh_token": "In+ooxlwIYEIiaCiK7WMivisBjo63n/cjDwG9DraELoQyo1ANJr9UxvqklIaMXWK" + , + "expires_in": 7200 + } + + access_token 访问中台服务用到的 token, 用 x-token 作为 key 放入请求头中 + refresh_token 刷新 token + expires_in 有效时间 + + */ +@Data +public class AccessTokenModel { + //访问中台服务用到的 token, 用 x-token 作为 key 放入请求头中 + private String access_token; + //刷新 token + private String refresh_token; + //有效时间 + private Long expires_in; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/data/MiddleResult.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/data/MiddleResult.java new file mode 100644 index 0000000..5e7c424 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/data/MiddleResult.java @@ -0,0 +1,26 @@ +package com.yfd.platform.uavsystem.data; + +import lombok.Data; + +import java.util.Map; + + +@Data +public class MiddleResult { + + /** + * 页码 + */ + Integer page=1; + + /** + * 每页数量 + */ + Integer perPage=20; + + /** + * 条件数组 + */ + Map filters; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/data/MiddleReturn.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/data/MiddleReturn.java new file mode 100644 index 0000000..0b22773 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/data/MiddleReturn.java @@ -0,0 +1,35 @@ +package com.yfd.platform.uavsystem.data; + +import lombok.Data; + + +@Data +public class MiddleReturn { + + + /** + * 状态码 ok:200 + */ + String status; + + /** + * 返回成功信息 + */ + String message; + + /** + * 返回失败信息 + */ + String error; + + /** + * 结果集 + */ + Object result; + + /** + * 个数 + */ + int count; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/enums/WMCenterUrlEnum.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/enums/WMCenterUrlEnum.java new file mode 100644 index 0000000..df88916 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/enums/WMCenterUrlEnum.java @@ -0,0 +1,120 @@ +package com.yfd.platform.uavsystem.enums; + +import lombok.Getter; + +import java.util.stream.Stream; + +/** + * 电网资源业务中台维护服务地址枚举 + * + * @author qigc + * @date 2023-04-26 17:17 + */ +@Getter +public enum WMCenterUrlEnum { + /** + * 获取28个维护服务token + */ + GET_TOKEN("获取28个维护服务token", "/psr-auth/oauth/accessToken", "00"), + /** + * 新增 + */ + INSPECTION_PLAN_SAVE("新增巡视计划", "/WMCenter/patrol/planCreate", "01"), + INSPECTION_TASK_SAVE("新增巡视任务", "/WMCenter/patrol/workOrderCreate", "02"), + INSPECTION_RECORDS_SAVE("新增巡视记录", "/WMCenter/patrol/recordCreate", "03"), + + /** + * 修改 + */ + INSPECTION_PLAN_EDIT("修改巡视计划", "/WMCenter/patrol/planUpdate", "10"), + INSPECTION_TASK_EDIT("修改巡视任务", "/WMCenter/patrol/workOrderUpdate", "11"), + INSPECTION_RECORDS_EDIT("修改巡视记录", "/WMCenter/patrol/recordUpdate", "12"), + + + /** + * 查询服务 + */ + INSPECTION_PLAN_QUERY("查询巡视计划", "/WMCenter/patrol/planList", "20"), + INSPECTION_TASK_QUERY("查询巡视任务", "/WMCenter/patrol/workOrderList", "21"), + INSPECTION_RECORDS_QUERY("查询巡视记录", "/WMCenter/patrol/recordList", "22"), + DEFECT_RECORDS_QUERY("查询缺陷记录", "/WMCenter/defect/inquireDefect", "23"), + + /** + * 查看 + */ + INSPECTION_PLAN_SHOW("查看巡视计划", "/WMCenter/patrol/planQuery", "30"), + INSPECTION_TASK_SHOW("查看巡视任务", "/WMCenter/patrol/workOrderQuery", "31"), + INSPECTION_RECORDS_SHOW("查看巡视记录", "/WMCenter/patrol/recodeQuery", "32"), + DEFECT_RECORDS_SHOW("查看缺陷记录", "/WMCenter/defect/viewDefect", "33"), + + + + /** + * 附件下载 + */ + FILE_DOWNLOAD("附件下载","/WMCenter/defect/record/fileDownload","40"), + ; + + + private final String name; + private final String url; + private final String code; + + WMCenterUrlEnum(String name, String url, String code) { + this.name = name; + this.url = url; + this.code = code; + } + + public String getValue() { + return this.url; + } + + public static WMCenterUrlEnum toCode(String code) { + return Stream.of(WMCenterUrlEnum.values()).filter(p -> p.code.equals(code)).findAny().orElse(null); + } + + public static WMCenterUrlEnum toUrlAndType(String code) { + return Stream.of(WMCenterUrlEnum.values()).filter(p -> p.code.equals(code)).findAny().orElse(null); + } + + /** + *

+ * 根据code获取名称 + *

+ * + * @param code 值 + * @return {@link String} 返回结果 + * @Author hu shi hua + * @date 2024/3/15 11:00 + * @version 1.0.0 + */ + public static String entryCodeReverName(String code) { + for (WMCenterUrlEnum s : values()) { + if (s.getCode().equals(code)) { + return s.getName(); + } + } + return ""; + } + + /** + *

+ * 根据url获取枚举信息 + *

+ * + * @param url + * @return {@link WMCenterUrlEnum} 返回结果 + * @Author hu shi hua + * @date 2024/3/15 11:01 + * @version 1.0.0 + */ + public static WMCenterUrlEnum entryCodeRever(String url) { + for (WMCenterUrlEnum s : values()) { + if (s.getUrl().equals(url)) { + return s; + } + } + return null; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/service/MiddlePlatformService.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/service/MiddlePlatformService.java new file mode 100644 index 0000000..087993b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/service/MiddlePlatformService.java @@ -0,0 +1,44 @@ +package com.yfd.platform.uavsystem.service; + + + +import com.yfd.platform.uavsystem.data.AccessTokenModel; +import com.yfd.platform.uavsystem.enums.WMCenterUrlEnum; + +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + + +public interface MiddlePlatformService { + + /** + *

+ * 查询操作 + *

+ * + * @param url 调用url + * @param json 新增参数 + * @return {@link String} 返回结果 + * @Author hu shi hua + * @date 2024/3/15 15:18 + * @version 1.0.0 + */ + Object queryOperate(WMCenterUrlEnum url, Map json); + + + /** + *

+ * 新增、修改、查看操作 + *

+ * + * @param url 调用url + * @param json 新增参数 + * @return {@link String} 返回结果 + * @Author hu shi hua + * @date 2024/3/15 15:13 + * @version 1.0.0 + */ + Object addOrEditOrShowOperate(WMCenterUrlEnum url, Map json); + + void downloadFile(WMCenterUrlEnum urlEnum, Object data, HttpServletResponse response); +} diff --git a/riis-system/src/main/java/com/yfd/platform/uavsystem/service/impl/MiddlePlatformServiceImpl.java b/riis-system/src/main/java/com/yfd/platform/uavsystem/service/impl/MiddlePlatformServiceImpl.java new file mode 100644 index 0000000..01bf0c2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/uavsystem/service/impl/MiddlePlatformServiceImpl.java @@ -0,0 +1,370 @@ +package com.yfd.platform.uavsystem.service.impl; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.yfd.platform.uavsystem.base.BaseApiService; +import com.yfd.platform.uavsystem.data.AccessTokenModel; +import com.yfd.platform.uavsystem.data.MiddleResult; +import com.yfd.platform.uavsystem.enums.WMCenterUrlEnum; +import com.yfd.platform.uavsystem.service.MiddlePlatformService; +import com.yfd.platform.utils.SystemUtils; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static com.yfd.platform.uavsystem.enums.WMCenterUrlEnum.*; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + +@RestController +@RequestMapping("/zxApi") +@Slf4j +public class MiddlePlatformServiceImpl extends BaseApiService implements MiddlePlatformService { + + @Value("${middle.crud.platform}") + private String middleHrefByCrud; + @Value("${middle.crud.tenantId}") + private String middleTenantIdByCrud; + @Value("${middle.crud.tenantKey}") + private String middleTenantKeyByCrud; + private Middle middle; + + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 是否启用 + */ + @Value("${middle.enable}") + private boolean enable; + + @Data + class Middle { + private String middleHref; + private String middleTenantId; + private String middleTenantKey; + } + + public void setMiddle() { + if (ObjectUtils.isEmpty(middle)) { + middle = new Middle(); + middle.setMiddleHref(middleHrefByCrud); + middle.setMiddleTenantId(middleTenantIdByCrud); + middle.setMiddleTenantKey(middleTenantKeyByCrud); + } + } + + /** + *

+ * 获取token + *

+ * + * @param refresh 是否需要重新获取 + * @return {@link AccessTokenModel} 报文头信息 + * @Author hu shi hua + * @date 2024/3/15 14:48 + * @version 1.0.0 + */ + @PostMapping("/getToken") + public AccessTokenModel getToken(Integer refresh) { + if (!enable) { + log.info("调用开关为false"); + return null; + } + setMiddle(); + if (ObjectUtil.isEmpty(middle)) { + log.info("urlMap未找到"); + return null; + } + //首先看看redis中有没有token + Object ztToken = redisTemplate.opsForValue().get("UAV_Token"); + if (ObjectUtil.isNotEmpty(ztToken)) { + //没过期 + if (ObjectUtil.isEmpty(refresh)) { + //不需要重新获取 + return JSON.parseObject(ztToken.toString(), AccessTokenModel.class); + } + } + + RestTemplate rest = new RestTemplate(); + String uri = WMCenterUrlEnum.GET_TOKEN.getValue(); + //获取token地址 + String reqData = "client_id=${tenantId}&client_secret=${tenantKey}&grant_type=credentials"; + String urlIp = "${gateway}${uri}"; + + String tenantKeyEncode = null; + + try { + tenantKeyEncode = URLEncoder.encode(middle.getMiddleTenantKey(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.error(e.getMessage());; + } + + log.info(tenantKeyEncode); + String url = urlIp + "?" + reqData; + //使用编码过的Key + url = url.replace("${gateway}", middle.getMiddleHref()) + .replace("${uri}", uri) + .replace("${tenantId}", middle.getMiddleTenantId()) + .replace("${tenantKey}", tenantKeyEncode); + log.info(url); + + // 将token放入redis中 + AccessTokenModel tokenModel = null; + tokenModel = rest.getForObject(url, AccessTokenModel.class); + if (tokenModel != null) { + //过期时间预留3分钟 + redisTemplate.opsForValue().set("UAV_Token", JSON.toJSONString(tokenModel), tokenModel.getExpires_in() - (60 * 3), TimeUnit.SECONDS); + } + return tokenModel; + } + + /** + *

+ * 查询操作 + *

+ * + * @param url 调用url + * @param json 新增参数 + * @return {@link String} 返回结果 + * @Author hu shi hua + * @date 2024/3/15 15:18 + * @version 1.0.0 + */ + @Override + @PostMapping("/search") + public Object queryOperate(WMCenterUrlEnum url, Map json) { + try { + MiddleResult filters = new MiddleResult(); + // 获取当前页 + Integer page = Integer.parseInt(json.get("page").toString()); + // 获取页大小 + Integer perPage = Integer.parseInt(json.get("perPage").toString()); + if (ObjectUtil.isNotEmpty(page)) { + filters.setPage(page); + } + if (ObjectUtil.isNotEmpty(perPage)) { + filters.setPerPage(perPage); + } + + Object filtersObj = json.get("filters"); + if (ObjectUtil.isNotEmpty(filtersObj)) { + Map filter = (Map) filtersObj; + filters.setFilters(filter); + } + return publicHttpObj(url, filters); + } catch (Exception ex) { + log.error("查询操作时解析json异常。。。。"); + log.error(ex.getMessage()); + log.error(ex.getMessage()); + } + return null; + } + + /** + *

+ * 新增、修改、查看操作 + *

+ */ + @Override + public Object addOrEditOrShowOperate(WMCenterUrlEnum url, Map json) { + return publicHttpObj(url, json); + } + + /** + * @param urlEnum 请求地址 + * @param filters 入参 + * @return + */ + private Object publicHttpObj(WMCenterUrlEnum urlEnum, @RequestBody Object filters) { + setMiddle(); + if (!enable) { + return null; + } + + AccessTokenModel tokenObj = getToken(null); + String token = tokenObj.getAccess_token(); + String endUrl = middle.getMiddleHref() + urlEnum.getUrl(); + + String body = JSON.toJSONString(filters); + log.info("调用参数:" + body); + + Object o = null; + try { + o = postHttp(endUrl, filters, token); + } catch (Exception e) { + String error = e.getMessage(); + o = error; + if (error.contains("423")) { + getToken(1); + } + } + //日志 + return o; + } + + + private Object postHttp(String url, Object data, String token) { + RestTemplate rest = new RestTemplate(); + HttpHeaders header = new HttpHeaders(); + header.add("x-token", token); + header.add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8"); + String body = JSON.toJSONString(data); // 在此加入接口的请求参数实体 + HttpEntity entity = new HttpEntity(body, header); + Object o = null; + try { + o = rest.exchange(url, HttpMethod.POST, entity, Object.class);//可以指定服务响应对应的反序列化数据格式代替 Object + } catch (Exception e) { + log.error("调用异常:调用url:{},\n入参:{},\n返回:{},\n错误信息:{}", url, body, JSON.toJSONString(o), e.getMessage()); + String error = e.getMessage(); + throw new RuntimeException(error); + } + log.info("调用url:{},\n入参:{},\n返回:{}", url, body, JSON.toJSONString(o)); + return o; + } + + + /** + * 查询案例 + * @return + */ + @PostMapping("/query") + public Object query() { + String[] kind = new String[2]; + kind[0] = "02"; + Map map = new HashMap<>(); + map.put("page", "1"); + map.put("perPage", "1"); + map.put("orderBy", ""); + Map filters = new HashMap<>(); + filters.put("professionalKind", kind); + filters.put("ctimeEnd", "2023-09-31 17:32:18"); + filters.put("ctimeStart", "2023-01-01 17:32:18"); + map.put("filters", filters); + return queryOperate(INSPECTION_PLAN_QUERY, map); + } + + /** + * 新增测试 + * @return + */ + @PostMapping("/saveTest") + public Object saveTest(){ + Map map = new HashMap<>(); + map.put("completedDateTime",""); + map.put("containerId","73867B48-D9BE-492C-87CA-AA6812D98410-00005"); + map.put("createrId","F017B5E569335DA8E043621DE60A04B4"); + map.put("finishState","0"); + map.put("humidity","11"); + map.put("maintCrewId","16810251D14FC6DCE050E60A50273290"); + map.put("maintCrewName","变电运维一班"); + map.put("noFinishReason",""); + map.put("patrolCrewId","16810251D14FC6DCE050E60A50273290"); + map.put("patrolResult","未完成"); + map.put("patrolWorkId","8a80819c8e2e3e20018e303d66cf000e"); + map.put("patrolWorkTaskId","8a80819c8e2e3e20018e303e57ca0010"); + map.put("robotPatrolEndTime","2024-04-19 12:50:00"); + map.put("robotPatrolStartTime","2024-03-19 12:20:00"); + map.put("startedDateTime",""); + map.put("systemSource","无人机自主巡检微应用"); + map.put("systemSourceId","f6a37f99af0511ecb85bfa163ec6349c"); + map.put("temperature","0"); + map.put("weather","01"); + map.put("whetherUseApparatus","1"); + map.put("remark","56af264d8037483da511d5dfd70faf39,c4867f9c52934a028e170216ddd5a7cb"); + return addOrEditOrShowOperate(INSPECTION_RECORDS_SAVE, map); + } + + + + + public Map setDangerMap(List> list) { + + Map oneMap = new HashMap<>(); + + Map bodyMapOne = new HashMap<>(); + bodyMapOne.put("errors", "操作成功"); + + bodyMapOne.put("result", list); + bodyMapOne.put("pageSize", 1000); + bodyMapOne.put("totalCount", 2); + bodyMapOne.put("totalPage", 1); + + + oneMap.put("body", bodyMapOne); + + Map headersOne = new HashMap<>(); + List headersOneListContentType = new ArrayList(); + headersOneListContentType.add("application/json;charset=UTF-8"); + headersOne.put("Content-Type", headersOneListContentType); + List headersOneListContentLength = new ArrayList(); + headersOneListContentLength.add("1697"); + headersOne.put("Content-Length", headersOneListContentLength); + List headersOneListContentDate = new ArrayList(); + headersOneListContentDate.add("Fri, 06 Jan 2023 11:07:34 GMT"); + headersOne.put("Date", headersOneListContentDate); + oneMap.put("headers", headersOne); + oneMap.put("statusCode", "OK"); + oneMap.put("statusCodeValue", 200); + + return oneMap; + } + + @Override + public void downloadFile(WMCenterUrlEnum urlEnum, Object data, HttpServletResponse response) { + setMiddle(); + AccessTokenModel tokenObj = getToken(null); + String token = tokenObj.getAccess_token(); + String url = middle.getMiddleHref() + urlEnum.getUrl(); + String body = JSON.toJSONString(data); + log.info("调用参数:" + body); + RestTemplate rest = new RestTemplate(); + response.setCharacterEncoding("utf-8"); + response.setContentType(MULTIPART_FORM_DATA_VALUE); + HttpHeaders header = new HttpHeaders(); + header.add("x-token", token); + header.add(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_VALUE); + HttpEntity entity = new HttpEntity<>(body, header); + try { + // 响应类型不支持InputStream类型,使用Resource或byte[]接收三方文件服务返回的文件字节流 + org.springframework.core.io.Resource apiResult = rest.postForObject(url, entity, + org.springframework.core.io.Resource.class); + InputStream inputStream = apiResult.getInputStream(); + BufferedOutputStream out = FileUtil.getOutputStream("D:/test.jpeg"); + long copySize = IoUtil.copy(inputStream, out, IoUtil.DEFAULT_BUFFER_SIZE); + IoUtil.close(inputStream); + IoUtil.close(out); + } catch (RestClientException e) { + log.error("调用异常:调用url:{},\n入参:{},\n错误信息:{}", url, body, SystemUtils.getError(e)); + String error = SystemUtils.getError(e); + throw new RuntimeException(error); + } catch (IOException e) { + log.error(e.getMessage());; + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/CallBack.java b/riis-system/src/main/java/com/yfd/platform/utils/CallBack.java new file mode 100644 index 0000000..442b937 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/CallBack.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yfd.platform.utils; + +/** + * @date: 2020/6/9 17:02 + * @since: 1.0 + * @see {@link SpringContextHolder} + * 针对某些初始化方法,在SpringContextHolder 初始化前时,
+ * 可提交一个 提交回调任务。
+ * 在SpringContextHolder 初始化后,进行回调使用 + */ + +public interface CallBack { + /** + * 回调执行方法 + */ + void executor(); + + /** + * 本回调任务名称 + * @return / + */ + default String getCallBackName() { + return Thread.currentThread().getId() + ":" + this.getClass().getName(); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java b/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java new file mode 100644 index 0000000..dd9abe0 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/CodeGenerator.java @@ -0,0 +1,180 @@ +package com.yfd.platform.utils; + +import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.generator.AutoGenerator; +import com.baomidou.mybatisplus.generator.InjectionConfig; +import com.baomidou.mybatisplus.generator.config.*; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中 +@Slf4j +public class CodeGenerator { + + /** + * 自定义模板,模板引擎是 freemarker + */ + private static final String ENTITY_TEMPLATE_PATH = "/templates/entity.java.ftl"; + private static final String XML_TEMPLATE_PATH = "/templates/mapper.xml.ftl"; + private static final String MAPPER_TEMPLATE_PATH = "/templates/mapper.java.ftl"; + private static final String CONTROLLER_TEMPLATE_PATH = "/templates/controller.java.ftl"; + private static final String SERVICE_IMPL_TEMPLATE_PATH = "/templates/serviceImpl.java.ftl"; + private static final String SERVICE_TEMPLATE_PATH = "/templates/service.java.ftl"; + + /** + *

+ * 读取控制台内容 + *

+ */ + public static String scanner(String tip) { + Scanner scanner = new Scanner(System.in); + StringBuilder help = new StringBuilder(); + help.append("请输入" + tip + ":"); + log.info(help.toString()); + if (scanner.hasNext()) { + String ipt = scanner.next(); + if (StringUtils.isNotBlank(ipt)) { + return ipt; + } + } + throw new MybatisPlusException("请输入正确的" + tip + "!"); + } + + public static void main(String[] args) { + // 代码生成器 + AutoGenerator mpg = new AutoGenerator(); + + // 全局配置 + GlobalConfig gc = new GlobalConfig(); + String projectPath = System.getProperty("user.dir"); + gc.setOutputDir(projectPath + "/riis-system/src/main/java"); + gc.setAuthor("zhengsl"); + gc.setOpen(false); + // gc.setSwagger2(true); 实体属性 Swagger2 注解 + mpg.setGlobalConfig(gc); + + // 数据源配置 + DataSourceConfig dsc = new DataSourceConfig(); + dsc.setUrl("jdbc:mysql://43.138.168.68:3306/riisdb500_zhuangzhou?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai"); + dsc.setDriverName("com.mysql.cj.jdbc.Driver"); + dsc.setUsername("root"); + dsc.setPassword("ylfw20230626@"); + mpg.setDataSource(dsc); + + // 包配置 + PackageConfig pc = new PackageConfig(); + pc.setModuleName(scanner("模块名称")); + //pc.setParent("com.yfd.platform.modules"); + pc.setParent("com.yfd.platform.modules"); + pc.setEntity("domain"); + mpg.setPackageInfo(pc); + + // 自定义配置 + InjectionConfig cfg = new InjectionConfig() { + @Override + public void initMap() { + // to do nothing + } + }; + + // 如果模板引擎是 freemarker + String templatePath = "/templates/mapper.xml.ftl"; + // 如果模板引擎是 velocity + //String templatePath = "/templates/mapper.xml.vm"; + + // 自定义输出配置 + List focList = new ArrayList<>(); + // 自定义配置会被优先输出 + focList.add(new FileOutConfig(templatePath) { + @Override + public String outputFile(TableInfo tableInfo) { + // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! + return projectPath + "/riis-system/src/main/resources/mapper/" + pc.getModuleName() + + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; + } + }); + /* + cfg.setFileCreate(new IFileCreate() { + @Override + public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { + // 判断自定义文件夹是否需要创建 + checkDir("调用默认方法创建的目录,自定义目录用"); + if (fileType == FileType.MAPPER) { + // 已经生成 mapper 文件判断存在,不想重新生成返回 false + return !new File(filePath).exists(); + } + // 允许生成模板文件 + return true; + } + }); + */ + cfg.setFileOutConfigList(focList); + mpg.setCfg(cfg); + + // 配置模板 + TemplateConfig templateConfig = new TemplateConfig(); + + // 配置自定义输出模板 + //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 + focList.add(new FileOutConfig(MAPPER_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! + return projectPath + "/riis-system/src/main/java/com/yfd/platform/modules/" + pc.getModuleName() + + "/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_JAVA; + } + }); + focList.add(new FileOutConfig(CONTROLLER_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! + return projectPath + "/riis-system/src/main/java/com/yfd/platform/modules/" + pc.getModuleName() + + "/controller/" + tableInfo.getEntityName() + "Controller" + StringPool.DOT_JAVA; + } + }); + + focList.add(new FileOutConfig(SERVICE_IMPL_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! + return projectPath + "/riis-system/src/main/java/com/yfd/platform/modules/" + pc.getModuleName() + + "/service/impl/" + tableInfo.getEntityName() + "ServiceImpl" + StringPool.DOT_JAVA; + } + }); +// templateConfig.setEntity(ENTITY_TEMPLATE_PATH); +// templateConfig.setService(SERVICE_TEMPLATE_PATH); +// templateConfig.setServiceImpl(SERVICE_IMPL_TEMPLATE_PATH); +// templateConfig.setController(CONTROLLER_TEMPLATE_PATH); + + templateConfig.setXml(null); + mpg.setTemplate(templateConfig); + + // 策略配置 + StrategyConfig strategy = new StrategyConfig(); + strategy.setNaming(NamingStrategy.underline_to_camel); + strategy.setColumnNaming(NamingStrategy.underline_to_camel); + //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!"); + strategy.setEntityLombokModel(true); + strategy.setRestControllerStyle(true); + // 公共父类 + //strategy.setSuperControllerClass("BaseController"); + // 写于父类中的公共字段 + //strategy.setSuperEntityColumns("id"); + //rca_project,rca_projectanalysisrd,rca_projectinvestigaterd,rca_projectreport,rca_projectsummary,rca_projecttarget,rca_projecttrackrd,rca_analysisguide,rca_eventeffect,rca_failurecase,rca_failurecause,rca_failureclass,rca_failuremode + strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); + strategy.setControllerMappingHyphenStyle(true); + strategy.setTablePrefix("iis_"); + mpg.setStrategy(strategy); + mpg.setTemplateEngine(new FreemarkerTemplateEngine()); + mpg.execute(); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/DateUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/DateUtils.java new file mode 100644 index 0000000..3981256 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/DateUtils.java @@ -0,0 +1,48 @@ +package com.yfd.platform.utils; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * + * @Date: 2023/5/24 13:51 + * @Description: 日期工具类 + */ +public class DateUtils { + + /********************************** + * 用途说明: 根据当前日期获取当前一周的所有日期 + * 参数说明 date 时间 + * 返回值说明: java.util.List + ***********************************/ + public static List getWeekDays(Date date) { + // 获取日历实例,默认日期时间为当前,可根据具体的业务场景修改日期时间 + Calendar calendar = Calendar.getInstance(); + // 设置日期 + calendar.setTime(date); + //calendar.set(Calendar.YEAR, 2023); + //calendar.set(Calendar.MONTH, 4); + //calendar.set(Calendar.DAY_OF_MONTH, 24); + + // 设置日历日期为本周的周一 + // 循环取当前的星期和周一做比对,如果不是周一,设置日历往过去推一天,直到设置周一成功 + while (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) { + calendar.add(Calendar.DAY_OF_MONTH, -1); + } + + //当前周日期数组 + String[] currentWeekDays = new String[7]; + + //日期格式化 + DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + currentWeekDays[0] = df.format(calendar.getTime()); + + for (int i = 1; i < currentWeekDays.length; i++) { + calendar.add(Calendar.DAY_OF_MONTH, 1); + currentWeekDays[i] = df.format(calendar.getTime()); + } + return new ArrayList<>(Arrays.asList(currentWeekDays)); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/EncryptUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/EncryptUtils.java new file mode 100644 index 0000000..11e507e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/EncryptUtils.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.nio.charset.StandardCharsets; + +/** + * 加密 + * + * @date 2018-11-23 + */ + +public class EncryptUtils { + + private static final String STR_PARAM = "Passw0rd"; + + private static Cipher cipher; + + private static final IvParameterSpec IV = new IvParameterSpec(STR_PARAM.getBytes(StandardCharsets.UTF_8)); + + private static DESKeySpec getDesKeySpec(String source) throws Exception { + if (source == null || source.length() == 0){ + return null; + } + cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + String strKey = "Passw0rd"; + return new DESKeySpec(strKey.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 对称加密 + */ + public static String desEncrypt(String source) throws Exception { + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV); + return byte2hex( + cipher.doFinal(source.getBytes(StandardCharsets.UTF_8))).toUpperCase(); + } + + /** + * 对称解密 + */ + public static String desDecrypt(String source) throws Exception { + byte[] src = hex2byte(source.getBytes(StandardCharsets.UTF_8)); + DESKeySpec desKeySpec = getDesKeySpec(source); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = keyFactory.generateSecret(desKeySpec); + cipher.init(Cipher.DECRYPT_MODE, secretKey, IV); + byte[] retByte = cipher.doFinal(src); + return new String(retByte); + } + + private static String byte2hex(byte[] inStr) { + String stmp; + StringBuilder out = new StringBuilder(inStr.length * 2); + for (byte b : inStr) { + stmp = Integer.toHexString(b & 0xFF); + if (stmp.length() == 1) { + // 如果是0至F的单位字符串,则添加0 + out.append("0").append(stmp); + } else { + out.append(stmp); + } + } + return out.toString(); + } + + private static byte[] hex2byte(byte[] b) { + int size = 2; + if ((b.length % size) != 0){ + throw new IllegalArgumentException("长度不是偶数"); + } + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += size) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/ExecutionJob.java b/riis-system/src/main/java/com/yfd/platform/utils/ExecutionJob.java new file mode 100644 index 0000000..1c72c3e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/ExecutionJob.java @@ -0,0 +1,102 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import com.yfd.platform.config.MessageConfig; +import com.yfd.platform.config.thread.ThreadPoolExecutorUtil; +import com.yfd.platform.system.domain.Message; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.service.IMessageService; +import com.yfd.platform.system.service.IQuartzJobService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.JobExecutionContext; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import javax.annotation.Resource; +import java.sql.Timestamp; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 参考人人开源,https://gitee.com/renrenio/renren-security + * + * @date 2019-01-07 + */ +@Async +@SuppressWarnings({"unchecked", "all"}) +@Slf4j +public class ExecutionJob extends QuartzJobBean { + + /** + * 该处仅供参考 + */ + public final static ThreadPoolExecutor EXECUTOR = + ThreadPoolExecutorUtil.getPoll(); + + @Resource + private IMessageService messageService; + + @Resource + private MessageConfig messageConfig; + + @Override + public void executeInternal(JobExecutionContext context) { + QuartzJob quartzJob = + (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); + // 获取spring bean + IQuartzJobService quartzJobService = + SpringContextHolder.getBean(IQuartzJobService.class); + String uuid = quartzJob.getId(); + long startTime = System.currentTimeMillis(); + String jobName = quartzJob.getJobName(); + try { + // 执行任务 + log.info("任务开始执行,任务名称:" + jobName); + QuartzRunnable task = new QuartzRunnable(quartzJob.getJobClass(), + quartzJob.getJobMethod(), + quartzJob.getJobParams()); + Future future = EXECUTOR.submit(task); + future.get(); + long times = System.currentTimeMillis() - startTime; + + Message message = new Message(); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + message.setType("1"); + message.setTitle(quartzJob.getJobName()); + message.setContent(quartzJob.getDescription()); + message.setSenderName("定时器"); + message.setReceiverCodes(quartzJob.getOrderno().toString()); + message.setReceiverNames(""); + message.setStatus("1"); + message.setValidperiod(24); + messageConfig.addMessage(message); + // 任务状态 + log.info("任务执行完毕,任务名称:" + jobName + ", " + + "执行时间:" + times + "毫秒"); + log.info( + "--------------------------------------------------------------"); + } catch (Exception e) { + log.error("任务执行失败,任务名称:" + jobName); + log.error( + "--------------------------------------------------------------"); + quartzJob.setStatus("0"); + //更新状态 + quartzJobService.updateById(quartzJob); + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/FTPPathToolkit.java b/riis-system/src/main/java/com/yfd/platform/utils/FTPPathToolkit.java new file mode 100644 index 0000000..3a1635f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/FTPPathToolkit.java @@ -0,0 +1,77 @@ +package com.yfd.platform.utils; + +import java.io.File; + +public class FTPPathToolkit { + + /** + *

Title:

+ *

Description:

+ */ + public FTPPathToolkit() { + super(); + // TODO Auto-generated constructor stub + } + /** + * + * @Title: formatPath4File + * @Description:格式化文件路径,将其中不规范的分隔转换为标准的分隔符,并且去掉末尾的文件路径分隔符。 + * 本方法操作系统自适应 + * @param @param path + * @param @return + * @return String + * @throws + */ + public static String formatPathFile(String path) { + String reg0 = "\\\\+"; + String reg = "\\\\+|/+"; + String temp = path.trim().replaceAll(reg0, "/"); + temp = temp.replaceAll(reg, "/"); + if (temp.length() > 1 && temp.endsWith("/")) { + temp = temp.substring(0, temp.length() - 1); + } + temp = temp.replace('/', File.separatorChar); + return temp; + } + /** + * + * @Title: formatPathFTP + * @Description: 格式化文件路径,将其中不规范的分隔转换为标准的分隔符 , + * 并且去掉末尾的"/"符号 + * @param @param path + * @param @return + * @return String + * @throws + */ + public static String formatPathFTP(String path) { + String reg0 = "\\\\+"; + String reg = "\\\\+|/+"; + String temp = path.trim().replaceAll(reg0, "/"); + temp = temp.replaceAll(reg, "/"); + if (temp.length() > 1 && temp.endsWith("/")) { + temp = temp.substring(0, temp.length() - 1); + } + return temp; + } + /** + * + * @Title: genParentPath4FTP + * @Description: 获取FTP路径的父路径,但不对路径有效性做检查 + * @param @param path + * @param @return + * @return String + * @throws + */ + public static String genParentPathFTP(String path) { + String parentPath = new File(path).getParent(); + if (parentPath == null) { + return null; + } else { + return formatPathFTP(parentPath); + } + } + + + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/FileUtil.java b/riis-system/src/main/java/com/yfd/platform/utils/FileUtil.java new file mode 100644 index 0000000..79796dd --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/FileUtil.java @@ -0,0 +1,1129 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.hutool.poi.excel.BigExcelWriter; +import cn.hutool.poi.excel.ExcelUtil; +import cn.hutool.system.OsInfo; +import com.alibaba.fastjson.JSON; +import com.deepoove.poi.data.PictureType; +import com.google.zxing.*; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; +import com.yfd.platform.exception.BadRequestException; +import net.coobird.thumbnailator.Thumbnails; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.usermodel.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.awt.Color; +import java.awt.Font; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.security.MessageDigest; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.*; + +/** + * File工具类,扩展 hutool 工具包 + * + * + * @date 2018-12-27 + */ +public class FileUtil extends cn.hutool.core.io.FileUtil { + + private static final Logger log = LoggerFactory.getLogger(FileUtil.class); + + /** + * 系统临时目录 + *
+ * windows 包含路径分割符,但Linux 不包含, + * 在windows \\==\ 前提下, + * 为安全起见 同意拼装 路径分割符, + *
+     *       java.io.tmpdir
+     *       windows : C:\Users/xxx\AppData\Local\Temp\
+     *       linux: /temp
+     * 
+ */ + public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; + /** + * 定义GB的计算常量 + */ + private static final int GB = 1024 * 1024 * 1024; + /** + * 定义MB的计算常量 + */ + private static final int MB = 1024 * 1024; + /** + * 定义KB的计算常量 + */ + private static final int KB = 1024; + + /** + * 格式化小数 + */ + private static final DecimalFormat DF = new DecimalFormat("0.00"); + + public static final String IMAGE = "image"; + public static final String TXT = "document"; + public static final String MUSIC = "music"; + public static final String VIDEO = "video"; + public static final String OTHER = "other"; + + + /** + * MultipartFile转File + */ + public static File toFile(MultipartFile multipartFile) { + // 获取文件名 + String fileName = multipartFile.getOriginalFilename(); + // 获取文件后缀 + String prefix = "." + getExtensionName(fileName); + File file = null; + try { + // 用uuid作为文件名,防止生成的临时文件重复 + file = File.createTempFile(IdUtil.simpleUUID(), prefix); + // MultipartFile to File + multipartFile.transferTo(file); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return file; + } + + /** + * 获取文件扩展名,不带 . + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot > -1) && (dot < (filename.length()))) { + return filename.substring(0, dot); + } + } + return filename; + } + + /** + * 文件大小转换 + */ + public static String getSize(long size) { + String resultSize; + if (size / GB >= 1) { + //如果当前Byte的值大于等于1GB + resultSize = DF.format(size / (float) GB) + "GB "; + } else if (size / MB >= 1) { + //如果当前Byte的值大于等于1MB + resultSize = DF.format(size / (float) MB) + "MB "; + } else if (size / KB >= 1) { + //如果当前Byte的值大于等于1KB + resultSize = DF.format(size / (float) KB) + "KB "; + } else { + resultSize = size + "B "; + } + return resultSize; + } + + /** + * inputStream 转 File + */ + static File inputStreamToFile(InputStream ins, String name) throws Exception { + File file = new File(SYS_TEM_DIR + name); + if (file.exists()) { + return file; + } + OutputStream os = new FileOutputStream(file); + int bytesRead; + int len = 8192; + byte[] buffer = new byte[len]; + while ((bytesRead = ins.read(buffer, 0, len)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.close(); + ins.close(); + return file; + } + + /** + * 将文件名解析成文件的上传路径 + */ + public static File upload(MultipartFile file, String filePath) { + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyyMMddhhmmssS"); + String name = getFileNameNoEx(file.getOriginalFilename()); + String suffix = getExtensionName(file.getOriginalFilename()); + String nowStr = "-" + format.format(date); + try { + String fileName = name + "." + suffix; + String path = filePath +File.separator + fileName; + // getCanonicalFile 可解析正确各种路径 + File dest = new File(path).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + log.info("was not successful."); + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 将文件名解析成文件的上传路径 + * file 上传的文件 + * filePath 存储路径 + * tofilename 保存文件名称 + + */ + public static File upload(MultipartFile file, String filePath,String tofilename) { + try { + String filename = filePath + File.separator + tofilename; + File dest = new File(filename).getCanonicalFile(); + // 检测是否存在目录 + if (!dest.getParentFile().exists()) { + if (!dest.getParentFile().mkdirs()) { + } + } + // 文件写入 + file.transferTo(dest); + return dest; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + + /** + * 导出excel + */ + public static void downloadExcel(List> list, HttpServletResponse response) throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + String filename = "record" + cn.hutool.core.date.DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"); + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + // 一次性写出内容,使用默认样式,强制输出标题 + writer.write(list, true); + SXSSFSheet sheet = (SXSSFSheet) writer.getSheet(); + //上面需要强转SXSSFSheet 不然没有trackAllColumnsForAutoSizing方法 + sheet.trackAllColumnsForAutoSizing(); + //列宽自适应 + // writer.autoSizeColumnAll(); + sheet.setDefaultColumnWidth(20); + CellStyle headCellStyle = writer.getHeadCellStyle(); + org.apache.poi.ss.usermodel.Font headFont = writer.createFont(); + headFont.setFontName("宋体"); + //大小 + headFont.setFontHeightInPoints((short) 14); + //加粗 + headFont.setBold(true); + headCellStyle.setFont(headFont); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=" + filename + ".xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + /** + * 导出excel包含图片 + */ + public static void downloadExcelContainImg(int x, List> list, HttpServletResponse response) + throws IOException { + String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx"; + String filename = "record" + cn.hutool.core.date.DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"); + File file = new File(tempPath); + BigExcelWriter writer = ExcelUtil.getBigWriter(file); + //设置默认高度 + // 一次性写出内容,使用默认样式,强制输出标题 + for (int i = 0; i < list.size(); i++) { + if (Objects.isNull(list.get(i).get("column9"))) { + continue; + } + String imgPath = list.get(i).get("column9").toString(); + imgPath = URLDecoder.decode(imgPath, "utf-8"); + imgPath = "D:/riis/video/" + imgPath; + + boolean exist = FileUtil.exist(new File(imgPath)); + if (!exist) { + continue; + } + //读取图片 + byte[] pictureData = FileUtil.readBytes(imgPath); + //写入图片 + writePic(writer, x + 1, i + 1, pictureData, getImageType(imgPath)); + } + for (int i1 = 0; i1 < x; i1++) { + writer.setColumnWidth(i1, 20); + } + writer.write(list, true); + //response为HttpServletResponse对象 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"); + //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 + response.setHeader("Content-Disposition", "attachment;filename=" + filename + ".xlsx"); + ServletOutputStream out = response.getOutputStream(); + // 终止后删除临时文件 + file.deleteOnExit(); + writer.flush(out, true); + //此处记得关闭输出Servlet流 + IoUtil.close(out); + } + + /** + * 导出 + * + * @param list 数据列表 + * @param excelHeaders 表头 + * @param keys 导出字段属性 + * @param imgs 导出图片字段属性,用逗号隔开 + * @param fileName 文件名称 + * @param response 相应对象 + * @return + */ + public static void excelImg(List> list, String[] excelHeaders, String[] keys, String imgs, + String fileName, HttpServletResponse response) throws IOException { + // 获取数据列表 + // 创建一个工作簿,对应文件 + XSSFWorkbook workBook = new XSSFWorkbook(); + + // 创建一个sheet工作表 + String filename = "record" + cn.hutool.core.date.DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"); + XSSFSheet sheet = workBook.createSheet(filename); + // 设置表头单元格样式 + XSSFCellStyle headstyle = workBook.createCellStyle(); + // 设置居中 + headstyle.setAlignment(HorizontalAlignment.CENTER); + headstyle.setVerticalAlignment(VerticalAlignment.CENTER); + XSSFFont headFont = workBook.createFont(); + headFont.setFontHeight(14); + headFont.setBold(true); + headstyle.setFont(headFont); + + // 创建一般单元格样式 + XSSFCellStyle cellstyle = workBook.createCellStyle(); + cellstyle.setAlignment(HorizontalAlignment.CENTER); + cellstyle.setVerticalAlignment(VerticalAlignment.CENTER); + cellstyle.setWrapText(true); + XSSFFont cellFont = workBook.createFont(); + cellFont.setFontHeight(11); + cellstyle.setFont(cellFont); + XSSFRow headRow = sheet.createRow(0); + for (int i = 0; i < excelHeaders.length; i++) { + XSSFCell cell = headRow.createCell(i); + cell.setCellValue(excelHeaders[i]); + cell.setCellStyle(headstyle); + sheet.setColumnWidth(i, (20 * 256)); + } + // 创建内容 + XSSFRow row = null; + for (int rowIndex = 0; rowIndex < list.size(); rowIndex++) { + row = sheet.createRow(rowIndex + 1); + row.setHeight((short) (40 * 20)); + // 单元格 + XSSFCell cell = null; + String s = JSONUtil.toJsonStr(list.get(rowIndex)); + // 转成map + Map map = (Map) JSON.parse(s); + for (int j = 0; j < keys.length; j++) { + cell = row.createCell(j); + cell.setCellStyle(cellstyle); + String key = keys[j]; + if (StrUtil.isNotBlank(imgs) && imgs.equals(key)) { + cell.setCellValue(""); + try { + //URL photoFile = new URL((String) map.get(keys[j])); + // 先把读进来的图片放到一个ByteArrayOutputStream中,以便产生ByteArray + ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); + String originfilename = (String) map.get(keys[j]); + + String temp = StrUtil.subBefore(originfilename, "_", true); + String tofilename = temp + "_small.jpg"; + Thumbnails.of(originfilename) + .size(480, 480) + .toFile(tofilename); + File photoFile = new File(tofilename); + boolean exist = FileUtil.exist(photoFile); + if (!exist) { + throw new RuntimeException("暂无图片"); + } + //将图片读入BufferedImage对象 + BufferedImage bufferImg = ImageIO.read(photoFile); + // 将图片写入流中 + ImageIO.write(bufferImg, "jpg", byteArrayOut); + // 利用HSSFPatriarch将图片写入EXCEL + XSSFDrawing patriarch = sheet.createDrawingPatriarch(); + // 图片一导出到单元格I3-5中 列开始:8 行开始:2 列结束:9 行结束:5 + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, j, rowIndex + 1, j, rowIndex + 1); + anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); + // 插入图片内容 + Picture picture = patriarch.createPicture(anchor, workBook.addPicture(byteArrayOut + .toByteArray(), XSSFWorkbook.PICTURE_TYPE_JPEG)); + picture.resize(1.05, 1.10); + //将图片插入工作表 + } catch (Exception e) { + cell.setCellValue("暂无图片"); + } + } else { + cell.setCellValue((String) map.get(keys[j])); + } + } + } + OutputStream out = null; + try { + //最终已流的形式返回 + out = response.getOutputStream(); + response.setHeader("content-type", "application/octet-stream"); + response.setContentType("application/octet-stream"); + response.addHeader("Content-Disposition", + "attachment; filename=" + URLEncoder.encode(fileName + cn.hutool.core.date.DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") + ".xlsx", "UTF-8")); + workBook.write(out); + + } catch (Exception e) { + log.error(e.getMessage());; + } finally { + if (out != null) { + out.flush(); + out.close(); + } + } + } + + /** + * @param writer + * @param x 单元格x轴坐标 + * @param y 单元格y轴坐标 + * @param pictureData 图片二进制数据 + * @param picType 图片格式 + */ + private static void writePic(BigExcelWriter writer, int x, int y, byte[] pictureData, int picType) { + Sheet sheet = writer.getSheet(); + sheet.setDefaultRowHeight((short) 30); + Drawing drawingPatriarch = sheet.createDrawingPatriarch(); + //设置图片单元格位置 + ClientAnchor anchor = drawingPatriarch.createAnchor(0, 0, 0, 0, x, y, x + 1, y + 1); + //随单元格改变位置和大小 + //anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); + + //添加图片 + int pictureIndex = sheet.getWorkbook().addPicture(pictureData, picType); + sheet.setColumnHidden(x - 1, true); + drawingPatriarch.createPicture(anchor, pictureIndex); + } + + public static String getFileType(String type) { + String documents = "txt doc pdf ppt pps xlsx xls docx"; + String music = "mp3 wav wma mpa ram ra aac aif m4a"; + String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg"; + String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg"; + if (image.contains(type)) { + return IMAGE; + } else if (documents.contains(type)) { + return TXT; + } else if (music.contains(type)) { + return MUSIC; + } else if (video.contains(type)) { + return VIDEO; + } else { + return OTHER; + } + } + + public static void checkSize(long maxSize, long size) { + // 1M + int len = 1024 * 1024; + if (size > (maxSize * len)) { + throw new BadRequestException("文件超出规定大小"); + } + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(File file1, File file2) { + String img1Md5 = getMd5(file1); + String img2Md5 = getMd5(file2); + return img1Md5.equals(img2Md5); + } + + /** + * 判断两个文件是否相同 + */ + public static boolean check(String file1Md5, String file2Md5) { + return file1Md5.equals(file2Md5); + } + + private static byte[] getByte(File file) { + // 得到文件长度 + byte[] b = new byte[(int) file.length()]; + try { + InputStream in = new FileInputStream(file); + try { + log.info(in.read(b)+""); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } catch (FileNotFoundException e) { + log.error(e.getMessage(), e); + return null; + } + return b; + } + + private static String getMd5(byte[] bytes) { + // 16进制字符 + char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + try { + MessageDigest mdTemp = MessageDigest.getInstance("MD5"); + mdTemp.update(bytes); + byte[] md = mdTemp.digest(); + int j = md.length; + char[] str = new char[j * 2]; + int k = 0; + // 移位 输出字符串 + for (byte byte0 : md) { + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + /** + * 下载文件 + * + * @param request / + * @param response / + * @param file / + */ + public static void downloadFile(HttpServletRequest request, HttpServletResponse response, File file, boolean deleteOnExit) { + if (request != null) { + response.setCharacterEncoding(request.getCharacterEncoding()); + response.setContentType("application/octet-stream"); + } + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + response.setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + IOUtils.copy(fis, response.getOutputStream()); + response.flushBuffer(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } finally { + if (fis != null) { + try { + fis.close(); + if (deleteOnExit) { + file.deleteOnExit(); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + } + } + /** + * 预览PDF文件 + * + * @param filepath / + * @param response / + */ + public static void viewPDF(String filepath, HttpServletResponse response) throws IOException { + response.setContentType("application/pdf;charset=UTF-8"); + response.setHeader("Content-Disposition", "inline"); + File file = new File(filepath); + String originFileName = file.getName(); //中文编码 + // response.setCharacterEncoding("UTF-8"); + String showName = StrUtil.isNotBlank(originFileName) ? originFileName : file.getName(); + showName = URLDecoder.decode(showName, "UTF-8"); + // response.setHeader("Content-Disposition","inline;fileName="+new String(showName.getBytes(), + // "ISO8859-1")+";fileName*=UTF-8''"+ new String(showName.getBytes(), "ISO8859-1")); + FileInputStream fis = new FileInputStream(file); + // response.setHeader("content-type", "application/msword"); + // response.setContentType("application/pdf; charset=utf-8"); + ServletOutputStream outputStream = response.getOutputStream(); + IOUtils.copy(fis, outputStream); + fis.close(); + outputStream.close(); + } + + public static String getMd5(File file) { + return getMd5(getByte(file)); + } + + /** + * 获取网络图片流 + * + * @param url + * @return + */ + public static InputStream getImageStream(String url) { + try { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setReadTimeout(5000); + connection.setConnectTimeout(5000); + connection.setRequestMethod("GET"); + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + InputStream inputStream = connection.getInputStream(); + return inputStream; + } + } catch (IOException e) { + log.info("获取网络图片出现异常,图片路径为:" + url); + log.error(e.getMessage());; + } + return null; + } + + public static void convertBase64ToImage(String base64String, String outputFilePath) { + try { + // 解码Base64字符串 + byte[] imageBytes = Base64.getDecoder().decode(base64String); + + // 创建多级目录 + FileUtil.touch(outputFilePath); + + // 创建一个输入流 + try (InputStream inputStream = new ByteArrayInputStream(imageBytes)) { + // 创建一个输出流,将图像写入文件 + try (FileOutputStream outputStream = new FileOutputStream(outputFilePath)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + } + } catch (IOException e) { + log.error(e.getMessage());; + } + } + + /** + * 在原始图片上标注矩形框,并返回图片名称 + * + * @param originfilename,jsonStr + * @return + */ + public static void MarkImageRectangle(String originfilename,String jsonStr,String markedfilename) { + try { + log.info("pos:" + jsonStr); + BufferedImage image = ImageIO.read(new File(originfilename)); + Graphics g = image.getGraphics(); + Graphics2D g2 = (Graphics2D) g; //将g转换为Graphics2D + BasicStroke stroke = new BasicStroke(5.0f); //创建一个粗细为5的BasicStroke对象 + g2.setStroke(stroke); //设置画线的粗细 + g2.setColor(Color.RED);//画笔颜色 + + //: [{"coors": [{"x": 10,"y": 15}, {"x": 100,"y": 150},{"x": 110,"y": 170},{"x": 200, + //"y": 270}]},{"coors": [{"x": 30,"y": 20}, {"x": 150,"y": 200}]}] + //pos:[{"coors":[{"x":216,"y":480},{"x":584,"y":156}]}] + JSONArray rectangles= JSONUtil.parseArray(jsonStr); + for (int i = 0; i < rectangles.size(); i++) { + JSONObject jobj = rectangles.getJSONObject(i); + JSONArray points = jobj.getJSONArray("areas"); + if (points.size() == 1) { + //画笔颜色设置为绿色 + g2.setColor(Color.GREEN); + JSONObject point = points.getJSONObject(0); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); + BasicStroke stroke1 = new BasicStroke(3.0f); + g2.setStroke(stroke1); + Integer x = point.getInt("x"); + Integer y = point.getInt("y"); + // 画十字 + g2.drawLine(x - 10, y, x + 10, y); + g2.drawLine(x, y - 10, x, y + 10); + } else if (points.size() == 2) { + //矩形框 + JSONObject leftuppoint = points.getJSONObject(0); + JSONObject rightdownpoint = points.getJSONObject(1); + int width = rightdownpoint.getInt("x") - leftuppoint.getInt("x"); + int height = Math.abs(rightdownpoint.getInt("y") - leftuppoint.getInt("y")); + log.info("width" + width); + log.info("height" + height); + //矩形框(原点x坐标,原点y坐标,矩形的长,矩形的宽) + g2.drawRect(leftuppoint.getInt("x"), leftuppoint.getInt("y"), width, height); + } else { + //多边形 + int size = points.size(); + int[] x = new int[size]; + int[] y = new int[size]; + for (int j = 0; j < points.size(); j++) { + JSONObject point = points.getJSONObject(j); + x[i] = point.getInt("x"); + y[i] = point.getInt("y"); + } + g2.drawPolygon(x, y, points.size());//画多边形 + } + + } + // 释放资源 + g.dispose(); + g2.dispose(); + FileOutputStream out = new FileOutputStream(markedfilename);//输出图片的地址 + ImageIO.write(image, "jpg", out); + out.close(); + } catch (FileNotFoundException ex) { + log.error(ex.getMessage()); + } catch (IOException ex) { + log.error(ex.getMessage()); + } + } + + /** + * 在原始图片上标注矩形框,并返回图片名称 + * + * @param originfilename,jsonStr + * @return + */ + public static void MarkImageRectangle1(String originfilename,String jsonStr,String markedfilename) { + try { + log.info("pos:" + jsonStr); + BufferedImage image = ImageIO.read(new File(originfilename)); + Graphics g = image.getGraphics(); + Graphics2D g2 = (Graphics2D) g; //将g转换为Graphics2D + BasicStroke stroke = new BasicStroke(5.0f); //创建一个粗细为5的BasicStroke对象 + g2.setStroke(stroke); //设置画线的粗细 + g2.setColor(Color.RED);//画笔颜色 + + //: [{"coors": [{"x": 10,"y": 15}, {"x": 100,"y": 150},{"x": 110,"y": 170},{"x": 200, + //"y": 270}]},{"coors": [{"x": 30,"y": 20}, {"x": 150,"y": 200}]}] + //pos:[{"coors":[{"x":216,"y":480},{"x":584,"y":156}]}] + JSONArray rectangles= JSONUtil.parseArray(jsonStr); + for (int i = 0; i < rectangles.size(); i++) { + JSONObject jobj = rectangles.getJSONObject(i); + JSONArray points = jobj.getJSONArray("areas"); + if (points.size() == 1) { + //画笔颜色设置为绿色 + g2.setColor(Color.GREEN); + JSONObject point = points.getJSONObject(0); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); + BasicStroke stroke1 = new BasicStroke(3.0f); + g2.setStroke(stroke1); + Integer x = point.getInt("x"); + Integer y = point.getInt("y"); + // 画十字 + g2.drawLine(x - 10, y, x + 10, y); + g2.drawLine(x, y - 10, x, y + 10); + } else if (points.size() == 2) { + // 字体、样式和大小 + Font font = new Font("微软雅黑", Font.PLAIN, 20); + //画笔颜色设置为绿色 + g2.setFont(font); + // 要绘制的文字 + String text = "表盘破损"; + //矩形框 + JSONObject leftuppoint = points.getJSONObject(0); + JSONObject rightdownpoint = points.getJSONObject(1); + int width = rightdownpoint.getInt("x") - leftuppoint.getInt("x"); + int height = Math.abs(rightdownpoint.getInt("y") - leftuppoint.getInt("y")); + log.info("width" + width); + log.info("height" + height); + // 绘制缺陷文字 + g2.drawString(text, leftuppoint.getInt("x"), leftuppoint.getInt("y")-10); + //矩形框(原点x坐标,原点y坐标,矩形的长,矩形的宽) + g2.drawRect(leftuppoint.getInt("x"), leftuppoint.getInt("y"), width, height); + } else { + //多边形 + int size = points.size(); + int[] x = new int[size]; + int[] y = new int[size]; + for (int j = 0; j < points.size(); j++) { + JSONObject point = points.getJSONObject(j); + x[i] = point.getInt("x"); + y[i] = point.getInt("y"); + } + g2.drawPolygon(x, y, points.size());//画多边形 + } + + } + // 释放资源 + g.dispose(); + g2.dispose(); + FileOutputStream out = new FileOutputStream(markedfilename);//输出图片的地址 + ImageIO.write(image, "jpg", out); + out.close(); + } catch (FileNotFoundException ex) { + log.error(ex.getMessage()); + } catch (IOException ex) { + log.error(ex.getMessage()); + } + } + + /** + * 根据图片后缀返回图片类型 + * + * @param url + * @return + */ + public static PictureType getPictureType(String url) { + if (StrUtil.isBlank(url)) { + return null; + } + if (url.endsWith(".jpeg") || url.endsWith(".jpg")) { + return PictureType.JPEG; + } + if (url.endsWith(".png")) { + return PictureType.PNG; + } + return null; + } + + /** + * 根据图片后缀返回图片类型(excel) + * + * @param url + * @return + */ + public static int getImageType(String url) { + if (StrUtil.isBlank(url)) { + return 0; + } + + if (url.endsWith(".jpeg") || url.endsWith(".jpg")) { + return HSSFWorkbook.PICTURE_TYPE_JPEG; + } + if (url.endsWith(".png")) { + return HSSFWorkbook.PICTURE_TYPE_PNG; + } + return 0; + } + + /********************************** + * 用途说明: 解析二维码 + * 参数说明 file + * 返回值说明: java.lang.String + ***********************************/ + public static String resolveCodeByFile(String path) { + String content = null; + BufferedImage image; + try { + File file = new File(path); + boolean exist = FileUtil.exist(file); + if (!exist) { + return null; + } + image = ImageIO.read(file); + LuminanceSource source = new BufferedImageLuminanceSource(image); + Binarizer binarizer = new HybridBinarizer(source); + BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); + Map hints = new HashMap(); + hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); + Result result = new MultiFormatReader().decode(binaryBitmap, hints);//解码 + log.info("图片中内容: "); + log.info("content: " + result.getText()); + content = result.getText(); + } catch (IOException e) { + log.error(e.getMessage());; + } catch (NotFoundException e) { + //这里判断如果识别不了带LOGO的图片,重新添加上一个属性 + try { + File file = new File(path); + boolean exist = FileUtil.exist(file); + if (!exist) { + return null; + } + image = ImageIO.read(file); + LuminanceSource source = new BufferedImageLuminanceSource(image); + Binarizer binarizer = new HybridBinarizer(source); + BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); + Map hints = new HashMap<>(); + //设置编码格式 + hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); + //设置优化精度 + hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); + //设置复杂模式开启(我使用这种方式就可以识别微信的二维码了) + hints.put(DecodeHintType.PURE_BARCODE, Boolean.TYPE); + Result result = new MultiFormatReader().decode(binaryBitmap, hints);//解码 + log.info("图片中内容: "); + log.info("content: " + result.getText()); + content = result.getText(); + } catch (Exception e1) { + log.error(e1.getMessage()); + } + } + return content; + } + + /** + * 预览Image文件 + * + * @param filepath / + * @param response / + */ + public static void viewImage(String filepath, HttpServletResponse response) throws IOException { + response.setHeader("Content-type", "jpeg"); + response.setHeader("Content-Disposition", "inline; filename=111.jpg"); + File file = new File(filepath); + FileInputStream in = new FileInputStream(file); + ServletOutputStream out = response.getOutputStream(); + byte[] buf = new byte[1024]; + int len = 0; + while ((len=in.read(buf)) != -1) { + out.write(buf, 0, len); + } + in.close(); + out.close(); + + } + + /** + * 播放音频 + * + * @param filepath / + * @param response / + */ + public static void playAudio(String filepath, HttpServletResponse response) throws IOException { + response.setHeader("Content-type", "audio/wav"); + response.setHeader("Content-Disposition", "inline; filename=111.wav"); + File file = new File(filepath); + FileInputStream in = new FileInputStream(file); + ServletOutputStream out = response.getOutputStream(); + byte[] buf = new byte[1024]; + int len = 0; + while ((len=in.read(buf)) != -1) { + out.write(buf, 0, len); + } + in.close(); + out.close(); + } + + public static void addWaterMark(String srcImgPath, String tarImgPath, String waterMarkContent, + int pos_x,int pos_y, + Color markContentColor, + Font font) { + + try { + // 读取原图片信息 + File srcImgFile = new File(srcImgPath);//得到文件 + boolean exist = FileUtil.exist(srcImgFile); + if (!exist) { + return; + } + + Image srcImg = ImageIO.read(srcImgFile);//文件转化为图片 + int srcImgWidth = srcImg.getWidth(null);//获取图片的宽 + int srcImgHeight = srcImg.getHeight(null);//获取图片的高 + // 加水印 + BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g = bufImg.createGraphics(); + g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null); + g.setColor(markContentColor); //根据图片的背景设置水印颜色 + g.setFont(font); //设置字体 + + //画出水印 + if(pos_y>0) { + g.drawString(waterMarkContent, pos_x, pos_y); + }else{ + g.drawString(waterMarkContent, pos_x, srcImgHeight+pos_y); + } + + g.dispose(); + // 输出图片 + FileOutputStream outImgStream = new FileOutputStream(tarImgPath); + ImageIO.write(bufImg, "jpg", outImgStream); + outImgStream.flush(); + outImgStream.close(); + + } catch (Exception e) { + // TODO: handle exception + } + } + + public static String parseNodeToXML(List> mapList, String filePath, String Node) throws IOException { + StringBuilder xml = new StringBuilder(); + xml.append("\n"); + xml.append("<").append(Node).append(">\n"); + for (Map map : mapList) { + xml.append("\n"); + } + xml.append(""); + // 分离文件名和目录路径 + File file = new File(filePath); + File directory = file.getParentFile(); + + // 如果目录不存在,则创建它 + if (!directory.exists() && !directory.mkdirs()) { + throw new IOException("无法创建目录: " + directory.getAbsolutePath()); + } + + FileWriter writer = new FileWriter(filePath); + writer.write(xml.toString()); + writer.flush(); + writer.close(); + log.info("写入完成!"); + return filePath; + } + + + + public static void convertCsvToBinary(String csvFilePath,String binaryFilePath) { + try { + FileInputStream csvInputStream = new FileInputStream(csvFilePath); + FileOutputStream binaryOutputStream = new FileOutputStream(binaryFilePath); + + // 写入文件头部分(64个字节) + byte[] header = new byte[64]; + binaryOutputStream.write(header); + + // 逐行读取CSV文件,解析温度数据并转换为二进制写入 + BufferedReader csvReader = new BufferedReader(new InputStreamReader(csvInputStream)); + String line; + while ((line = csvReader.readLine()) != null) { + String[] temperatures = line.split(","); + for (String temperatureStr : temperatures) { + float temp = Float.parseFloat(temperatureStr); + int intValue = (int) (temp * 10); + short temperature = (short) intValue; + binaryOutputStream.write(temperature & 0xFF); // 低位 + binaryOutputStream.write((temperature >> 8) & 0xFF); // 高位 + } + } + + // 关闭流 + csvReader.close(); + csvInputStream.close(); + binaryOutputStream.close(); + + log.info("Conversion complete."); + } catch (IOException e) { + log.error(e.getMessage());; + } + + } + + public static void downloadFile(String fileURL, String saveDir, String fileName) { + try { + OsInfo osInfo = new OsInfo(); + saveDir = saveDir.replace("/", "\\"); + if (osInfo.isLinux()) { + saveDir = saveDir.replace("\\", "/"); + } + URL url = new URL(fileURL); + HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + int responseCode = httpConn.getResponseCode(); + + // 总是检查HTTP响应码 + if (responseCode == HttpURLConnection.HTTP_OK) { + String disposition = httpConn.getHeaderField("Content-Disposition"); + String contentType = httpConn.getContentType(); + int contentLength = httpConn.getContentLength(); + + // 提示信息 + log.info("Content-Type = " + contentType); + log.info("Content-Disposition = " + disposition); + log.info("Content-Length = " + contentLength); + log.info("filename=" + fileName); + log.info("fileURL=" + fileURL); + // 打开一个流以读取服务器上的文件数据 + InputStream inputStream = httpConn.getInputStream(); + String saveFilePath = saveDir + fileName; + log.info("saveFilePath=" + saveFilePath); + // 创建File对象 + // File file = new File(saveFilePath); + // File file = cn.hutool.core.io.FileUtil.touch(saveFilePath); + cn.hutool.core.io.FileUtil.mkdir(saveDir); + // 检查并创建父目录 + // if (!file.getParentFile().exists()) { + // if (!file.getParentFile().mkdirs()) { + // return; // 或者抛出异常,根据你的需求 + // } + // } + // 打开一个流以写入本地文件 + FileOutputStream outputStream = new FileOutputStream(saveFilePath); + + int bytesRead = -1; + byte[] buffer = new byte[4096]; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + outputStream.close(); + inputStream.close(); + + log.info("File downloaded"); + + } else { + log.error("No file to download. Server replied HTTP code: " + responseCode); + } + httpConn.disconnect(); + } catch (IOException e) { + log.error(e.getMessage());; + } + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/FtpClient.java b/riis-system/src/main/java/com/yfd/platform/utils/FtpClient.java new file mode 100644 index 0000000..be8aac7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/FtpClient.java @@ -0,0 +1,428 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPReply; +import org.apache.commons.net.ftp.FTPSClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +@Component +@Slf4j +public class FtpClient { + @Value("${parentserver.serverip}") + private String ftp_ip; + + @Value("${parentserver.ftpport}") + private int ftp_port; + + @Value("${parentserver.ftpusername}") + private String ftp_userName; + + @Value("${parentserver.ftppassword}") + private String ftp_userPassword; + + TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() { + + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + // TODO Auto-generated method stub + + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + // TODO Auto-generated method stub + + } + + public X509Certificate[] getAcceptedIssuers() { + // TODO Auto-generated method stub + return null; + } + + } }; + + + public boolean uploadFile(String localFileName, String remoteFileName) throws Exception { + log.info("进入uploadFile上传方法"); + boolean flag = false; + // 创建SSL上下文 + SSLContext sslContext = SSLContext.getInstance("TLS"); + // 自定义证书,忽略已过期证书 +// TrustManager[] trustAllCerts = new TrustManager[1]; +// TrustManager tm = nesendRobotCommand MyTrustManager(); +// trustAllCerts[0] = tm; + // 初始化SSL上下文,使用自定义的信任管理器 + sslContext.init(null, trustManager, new SecureRandom()); + + // 设置FTP客户端使用的SSL上下文 + // 创建客户端,选择Implicit模式 + FTPSClient ftpClient = new FTPSClient(true,sslContext); + + ftpClient.setNeedClientAuth(false); + ftpClient.setControlEncoding("UTF-8"); + try { + int reply; + log.info("建立ftp服务连接"); + ftpClient.connect(ftp_ip, ftp_port);// 连接FTP服务器 + log.info("登录ftp服务"); + ftpClient.login(ftp_userName, ftp_userPassword);// 登录 + reply = ftpClient.getReplyCode(); + log.info("登录ftp服务返回状态码为:" + reply); + if (!FTPReply.isPositiveCompletion(reply)) { + ftpClient.disconnect(); + return flag; + } + ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); + //设置为被动模式 + ftpClient.enterLocalPassiveMode(); + //originFilePath就是上传文件的文件名,建议使用生成的唯一命名,中文命名最好做转码 + // 创建一个File对象,指定要读取的文件路径 + File file = new File(localFileName); + // 创建一个FileInputStream对象,传入File对象 + InputStream input = new FileInputStream(file); + String remotepath = StrUtil.subBefore(remoteFileName, "/", true); + //remotepath= "/aaa/bbb"; + if (remotepath.contains("/")) { + log.info("===========================================创建文件目录==========================================="); + boolean b = mkdirPath(remotepath, ftpClient); + } + String utf8RemotePath = new String(remoteFileName.getBytes("UTF-8"), "ISO-8859-1"); + boolean a = ftpClient.storeFile(utf8RemotePath, input); + log.info("要上传的原始文件名为:" + remoteFileName + ", 上传结果:" + a); + input.close(); + ftpClient.logout(); + flag = true; + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } finally { + if (ftpClient.isConnected()) { + try { + ftpClient.disconnect(); + } catch (IOException ignored) { + + } + } + } + return flag; + } + + public boolean uploadFile(String ip, String port, String username, String password, String filePath, String roPath) throws NoSuchAlgorithmException, KeyManagementException { + log.info("进入uploadFile上传方法"); + boolean flag = false; + // 创建SSL上下文 + SSLContext sslContext = SSLContext.getInstance("TLS"); + // 自定义证书,忽略已过期证书 + TrustManager[] trustAllCerts = new TrustManager[1]; + TrustManager tm = new MyTrustManager(); + trustAllCerts[0] = tm; + // 初始化SSL上下文,使用自定义的信任管理器 + sslContext.init(null, trustAllCerts, null); + // 设置FTP客户端使用的SSL上下文 + // 创建客户端,选择Implicit模式 + FTPSClient ftpClient = new FTPSClient(true,sslContext); + ftpClient.setNeedClientAuth(false); + ftpClient.setControlEncoding("gbk"); + try { + int reply; + log.info("建立ftp服务连接"); + ftpClient.connect(ip, Integer.parseInt(port));// 连接FTP服务器 + log.info("登录ftp服务"); + ftpClient.login(username, password);// 登录 + reply = ftpClient.getReplyCode(); + log.info("登录ftp服务返回状态码为:" + reply); + if (!FTPReply.isPositiveCompletion(reply)) { + ftpClient.disconnect(); + return flag; + } + ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); + //设置为被动模式 + ftpClient.enterLocalPassiveMode(); + //originFilePath就是上传文件的文件名,建议使用生成的唯一命名,中文命名最好做转码 + // 创建一个File对象,指定要读取的文件路径 + File file = new File(filePath); + // 创建一个FileInputStream对象,传入File对象 + InputStream input = new FileInputStream(file); + String remotepath = StrUtil.subBefore(roPath, "/", true); + //remotepath= "/aaa/bbb"; + if (remotepath.contains("/")) { + boolean b = mkdirPath(remotepath, ftpClient); + } + boolean a = ftpClient.storeFile(roPath, input); + log.info("要上传的原始文件名为:" + roPath + ", 上传结果:" + a); + input.close(); + ftpClient.logout(); + flag = true; + } catch (IOException e) { + throw new RuntimeException(e.getMessage()); + } finally { + if (ftpClient.isConnected()) { + try { + ftpClient.disconnect(); + } catch (IOException ignored) { + + } + } + } + return flag; + } + + public boolean deleteFile(String filename) throws Exception { + log.info("进入uploadFile上传方法"); + boolean flag = false; + // 创建SSL上下文 + SSLContext sslContext = SSLContext.getInstance("TLS"); + // 自定义证书,忽略已过期证书 + TrustManager[] trustAllCerts = new TrustManager[1]; + TrustManager tm = new MyTrustManager(); + trustAllCerts[0] = tm; + // 初始化SSL上下文,使用自定义的信任管理器 + sslContext.init(null, trustAllCerts, null); + // 设置FTP客户端使用的SSL上下文 + // 创建客户端,选择Implicit模式 + FTPSClient ftpClient = new FTPSClient(true, sslContext); + ftpClient.setNeedClientAuth(false); + ftpClient.setControlEncoding("UTF-8"); + try { + // 连接FTP服务器 + ftpClient.connect(ftp_ip, ftp_port); + // 登录FTP服务器 + ftpClient.login(ftp_userName, ftp_userPassword); + // 验证FTP服务器是否登录成功 + int replyCode = ftpClient.getReplyCode(); + if (!FTPReply.isPositiveCompletion(replyCode)) { + return flag; + } + // 切换FTP目录 + ftpClient.dele(filename); + ftpClient.logout(); + flag = true; + } catch (Exception e) { + log.error(e.getMessage());; + } finally { + if (ftpClient.isConnected()) { + try { + ftpClient.logout(); + } catch (IOException e) { + + } + } + } + return flag; + } + + public void downloadFile(String remoteFilePath, String localFilePath) throws IOException { + // 第二个参数true表示使用隐式SSL + FTPSClient ftps = new FTPSClient(true); + ftps.connect(ftp_ip, ftp_port); + + if (!FTPReply.isPositiveCompletion(ftps.getReplyCode())) { + ftps.disconnect(); + throw new IOException("连接ftp失败"); + } + + if (!ftps.login(ftp_userName, ftp_userPassword)) { + ftps.logout(); + ftps.disconnect(); + throw new IOException("FTP登录失败"); + } + + // 设置为被动模式 + ftps.enterLocalPassiveMode(); + // 设置文件类型 + ftps.setFileType(FTPClient.BINARY_FILE_TYPE); + FileUtil.touch(localFilePath); + String utf8RemotePath = new String(remoteFilePath.getBytes("UTF-8"), "ISO-8859-1"); + + // 下载文件 + try (OutputStream outputStream = new FileOutputStream(localFilePath)) { + boolean success = ftps.retrieveFile(utf8RemotePath, outputStream); + if (!success) { + throw new IOException("没有发现文件 " + remoteFilePath); + } + } + + ftps.logout(); + ftps.disconnect(); + } + + public boolean uploadFileNew(String localFileName, String remoteFileName) throws Exception { + log.info("进入uploadFile上传方法"); + // 第二个参数true表示使用隐式SSL + FTPSClient ftps = new FTPSClient(true); + ftps.connect(ftp_ip, ftp_port); + + if (!FTPReply.isPositiveCompletion(ftps.getReplyCode())) { + ftps.disconnect(); + throw new IOException("连接ftp失败"); + } + + if (!ftps.login(ftp_userName, ftp_userPassword)) { + ftps.logout(); + ftps.disconnect(); + throw new IOException("FTP登录失败"); + } + + // 设置为被动模式 + ftps.enterLocalPassiveMode(); + // 设置文件类型 + ftps.setFileType(FTPClient.BINARY_FILE_TYPE); +// FileUtil.touch(localFileName); + String utf8RemotePath = new String(remoteFileName.getBytes("UTF-8"), "ISO-8859-1"); + //originFilePath就是上传文件的文件名,建议使用生成的唯一命名,中文命名最好做转码 + // 创建一个File对象,指定要读取的文件路径 + File file = new File(localFileName); + // 创建一个FileInputStream对象,传入File对象 + InputStream input = new FileInputStream(file); + String remotepath = StrUtil.subBefore(utf8RemotePath, "/", true); + //remotepath= "/aaa/bbb"; + if (remotepath.contains("/")) { + mkdirPath(remotepath, ftps); + } + boolean a = ftps.storeFile(utf8RemotePath, input); + log.info("要上传的原始文件名为:" + utf8RemotePath + ", 上传结果:" + a); + input.close(); + ftps.logout(); + return true; + } + + public String readFileContent(String remoteFilePath) { + FTPSClient ftpClient = new FTPSClient(true); + ftpClient.setNeedClientAuth(false); + ftpClient.enterLocalPassiveMode(); + ftpClient.setControlEncoding("UTF-8"); + StringBuilder xmlContent = new StringBuilder(); + try { + // 连接FTP服务器 + ftpClient.connect(ftp_ip, ftp_port); + // 登录FTP服务器 + ftpClient.login(ftp_userName, ftp_userPassword); + // 验证FTP服务器是否登录成功 + int replyCode = ftpClient.getReplyCode(); + if (!FTPReply.isPositiveCompletion(replyCode)) { + return ""; + } + // 获取文件内容 + InputStream inputStream = ftpClient.retrieveFileStream(remoteFilePath); + if (inputStream != null) { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line; + + while ((line = reader.readLine()) != null) { + xmlContent.append(line); + } + reader.close(); + inputStream.close(); + + } + ftpClient.logout(); + log.info("文件获取完成!!!"); + } catch (Exception e) { + log.error(e.getMessage());; + } finally { + if (ftpClient.isConnected()) { + try { + ftpClient.logout(); + } catch (IOException ignored) { + + } + } + } + return xmlContent.toString(); + } + + /** + * @param path ftp服务器文件目录 + * @param ftp FTPClient对象 + * @return 创建状态 + */ + public boolean mkdirPath(String path, FTPClient ftp) { + try { + String directory = path.endsWith("/") ? path : path + "/"; + if (!directory.equalsIgnoreCase("/") && !ftp.changeWorkingDirectory(directory)) { + // 如果远程目录不存在,则递归创建远程服务器目录 + int start = 0; + int end; + if (directory.startsWith("/")) { + start = 1; + } + end = directory.indexOf("/", start); + do { + String subDirectory = path.substring(0, end); + if (!ftp.changeWorkingDirectory(subDirectory)) { + if (ftp.makeDirectory(subDirectory)) { + if (!ftp.changeWorkingDirectory(subDirectory)) { + return false; + } + } else { + return false; + } + } + start = end + 1; + end = directory.indexOf("/", start); + } while (end > start); + } + return true; + } catch (Exception exception) { + log.error(exception.getMessage()); + return false; + } + } + + public boolean uploadfileSystem(String ip,String port,String name,String password,String localFileName, String remoteFileName) throws IOException { + + log.info("进入uploadFile上传方法"); + // 第二个参数true表示使用隐式SSL + FTPSClient ftps = new FTPSClient(true); + ftps.connect(ip, NumberUtil.parseInt(port)); + + if (!FTPReply.isPositiveCompletion(ftps.getReplyCode())) { + ftps.disconnect(); + throw new IOException("连接ftp失败"); + } + + if (!ftps.login(name, password)) { + ftps.logout(); + ftps.disconnect(); + throw new IOException("FTP登录失败"); + } + + // 设置为被动模式 + ftps.enterLocalPassiveMode(); + // 设置文件类型 + ftps.setFileType(FTPClient.BINARY_FILE_TYPE); +// FileUtil.touch(localFileName); + String utf8RemotePath = new String(remoteFileName.getBytes("UTF-8"), "ISO-8859-1"); + //originFilePath就是上传文件的文件名,建议使用生成的唯一命名,中文命名最好做转码 + // 创建一个File对象,指定要读取的文件路径 + File file = new File(localFileName); + // 创建一个FileInputStream对象,传入File对象 + InputStream input = new FileInputStream(file); + String remotepath = StrUtil.subBefore(utf8RemotePath, "/", true); + //remotepath= "/aaa/bbb"; + if (remotepath.contains("/")) { + mkdirPath(remotepath, ftps); + } + boolean a = ftps.storeFile(utf8RemotePath, input); + log.info("要上传的原始文件名为:" + utf8RemotePath + ", 上传结果:" + a); + input.close(); + ftps.logout(); + return true; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/GroupAndProcessParallel.java b/riis-system/src/main/java/com/yfd/platform/utils/GroupAndProcessParallel.java new file mode 100644 index 0000000..2345d19 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/GroupAndProcessParallel.java @@ -0,0 +1,30 @@ +package com.yfd.platform.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public class GroupAndProcessParallel { + + public static Map data = new ConcurrentHashMap<>(); + + + public static List>> distributeToTenCollections1(Map>> groupedByCode) { + List>> collections = new ArrayList<>(6); + for (int i = 0; i < 6; i++) { + collections.add(new ArrayList<>()); + } + + // 负载均衡策略:轮询分配 + int index = 0; + for (List> objects : groupedByCode.values()) { + collections.get(index++ % 6).addAll(objects); + } + + return collections; + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/HttpRESTfulUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/HttpRESTfulUtils.java new file mode 100644 index 0000000..67c2d84 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/HttpRESTfulUtils.java @@ -0,0 +1,789 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.config.AlgorithmManufacturerConfig; +import com.yfd.platform.config.HttpServerConfig; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.patroltask.service.IAlarmLogService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import okhttp3.*; +import okhttp3.logging.HttpLoggingInterceptor; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URLDecoder; +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Component +public class HttpRESTfulUtils { + + private final static Logger logger = LoggerFactory.getLogger(HttpRESTfulUtils.class); + + private OkHttpClient client; + + @Resource + private HttpServerConfig Config; + @Resource + private RtspToMP4 rtspToMP4; + @Resource + private AlgorithmManufacturerConfig algorithmManufacturerConfig; + + public interface RequestCallback { + + void run(JSONObject response); + } + + private OkHttpClient getClient() { + if (client == null) { + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); + //todo 暂时写死超时时间 均为5s + // 设置连接超时时间 + httpClientBuilder.connectTimeout(30, TimeUnit.SECONDS); + // 设置读取超时时间 + httpClientBuilder.readTimeout(20, TimeUnit.SECONDS); + // 设置连接池 + httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); + if (logger.isDebugEnabled()) { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { + logger.debug("http请求参数:" + message); + }); + logging.setLevel(HttpLoggingInterceptor.Level.BASIC); + // OkHttp進行添加攔截器loggingInterceptor + httpClientBuilder.addInterceptor(logging); + } + client = httpClientBuilder.build(); + } + return client; + + } + + public JSONObject sendHeaderHttpPost(String posttype, String ip, String port, String secret, String token, + String api, Map param, RequestCallback callback) { + OkHttpClient client = getClient(); + String url = String.format("http://%s:%s/%s", ip, port, api); + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code", -2); + responseJSON.put("msg", "http请求调用失败!"); + + RequestBody body = null; + if (posttype.equals("json")) { + body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(param)); + } else { + FormBody.Builder builder = new FormBody.Builder(); + builder.add("secret", secret); + if (param != null && param.keySet().size() > 0) { + for (String key : param.keySet()) { + if (param.get(key) != null) { + builder.add(key, param.get(key).toString()); + } + } + } + body = builder.build(); + } + Request request = new Request.Builder() + .addHeader("token", token) + .post(body) + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON = JSON.parseObject(responseStr); + } + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + //throw new RuntimeException(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + //logger.error(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + } + + } catch (Exception e) { + logger.error(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + } + } else { + client.newCall(request).enqueue(new Callback() { + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + logger.error(String.format("读取Http服务器数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + } + } + }); + } + + return responseJSON; + } + + public JSONObject sendHttpPost(String posttype, String ip, String port, String secret, String api, Map param, RequestCallback callback) { + OkHttpClient client = getClient(); + String url = String.format("http://%s:%s/%s", ip, port, api); + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code", -2); + responseJSON.put("msg", "http请求调用失败!"); + + RequestBody body = null; + if (posttype.equals("json")) { + body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(param)); + } else { + FormBody.Builder builder = new FormBody.Builder(); + builder.add("secret", secret); + if (param != null && param.keySet().size() > 0) { + for (String key : param.keySet()) { + if (param.get(key) != null) { + builder.add(key, param.get(key).toString()); + } + } + } + body = builder.build(); + } + Request request = new Request.Builder() + .post(body) + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON = JSON.parseObject(responseStr); + } + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + //throw new RuntimeException(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + //logger.error(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + } + + } catch (Exception e) { + logger.error(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + } + } else { + client.newCall(request).enqueue(new Callback() { + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + logger.error(String.format("读取Http服务器数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + } + } + }); + } + + return responseJSON; + } + + public JSONObject sendHttpPostStr(String ip, String port, String api, Map param, RequestCallback callback) { + OkHttpClient client = getClient(); + String url = String.format("http://%s:%s/%s", ip, port, api); + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code", -2); + responseJSON.put("msg", "http请求调用失败!"); + RequestBody body = null; + FormBody.Builder builder = new FormBody.Builder(); + if (param != null && param.keySet().size() > 0) { + for (String key : param.keySet()) { + if (param.get(key) != null) { + builder.add(key, param.get(key).toString()); + } + } + } + body = builder.build(); + + Request request = new Request.Builder() + .post(body) + .addHeader("Cookie", "Name=admin; Pass=Admin123; Type=Admin") + .addHeader("Content-Type", "text/plain") + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON.put("code", 200); + responseJSON.put("msg", "成功!"); + responseJSON.put("data", URLDecoder.decode(responseStr, "utf-8")); + } + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + //throw new RuntimeException(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + //logger.error(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + } + + } catch (Exception e) { + logger.error(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + } + } else { + client.newCall(request).enqueue(new Callback() { + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + logger.error(String.format("读取Http服务器数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + } + } + }); + } + + return responseJSON; + } + + public JSONObject sendHttpGetStr(String ip, String port, String api, RequestCallback callback) { + OkHttpClient client = getClient(); + String url = String.format("http://%s:%s/%s", ip, port, api); + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code", -2); + responseJSON.put("msg", "http请求调用失败!"); + Request request = new Request.Builder() + .addHeader("Cookie", "Name=admin; Pass=Admin123; Type=Admin") + .addHeader("Content-Type", "text/plain") + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON.put("code", 200); + responseJSON.put("msg", "成功!"); + responseJSON.put("data", URLDecoder.decode(responseStr, "utf-8")); + } + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + //throw new RuntimeException(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + //logger.error(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("读取Http服务器数据失败: %s, %s", url, e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("连接Http服务器失败: %s, %s", url, e.getMessage())); + } + + } catch (Exception e) { + logger.error(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + throw new RuntimeException(String.format("访问Http服务器失败: %s, %s", url, e.getMessage())); + } + } else { + client.newCall(request).enqueue(new Callback() { + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) { + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + } else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + + if (e instanceof SocketTimeoutException) { + //读取超时超时异常 + logger.error(String.format("读取Http服务器数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if (e instanceof ConnectException) { + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接Http服务器失败: %s, %s", call.request().toString(), e.getMessage())); + } + } + }); + } + + return responseJSON; + } + + public void sendGetForImg(String ip, String port, String secret, String api, Map params, + String targetPath, String fileName) { + String url = String.format("http://%s:%s/%s", ip, port, api); + logger.debug(url); + HttpUrl parseUrl = HttpUrl.parse(url); + if (parseUrl == null) { + return; + } + HttpUrl.Builder httpBuilder = parseUrl.newBuilder(); + + httpBuilder.addQueryParameter("secret", secret); + if (params != null) { + for (Map.Entry param : params.entrySet()) { + httpBuilder.addQueryParameter(param.getKey(), param.getValue().toString()); + } + } + Request request = new Request.Builder() + .url(httpBuilder.build()) + .build(); + logger.info(request.toString()); + try { + OkHttpClient client = getClient(); + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + if (targetPath != null) { + File snapFolder = new File(targetPath); + if (!snapFolder.exists()) { + if (!snapFolder.mkdirs()) { + logger.warn("{}路径创建失败", snapFolder.getAbsolutePath()); + } + + } + File snapFile = new File(targetPath + File.separator + fileName); + FileOutputStream outStream = new FileOutputStream(snapFile); + + outStream.write(Objects.requireNonNull(response.body()).bytes()); + outStream.flush(); + outStream.close(); + } else { + logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + } + Objects.requireNonNull(response.body()).close(); + } else { + logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message())); + } + response.close(); + } catch (ConnectException e) { + logger.error(String.format("连接服务器失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + } + + //调用摄像头预置位 + public JSONObject callDevicePos(String devicecode, String channelcode, String poscode, RequestCallback callback) { + String api = String.format("api/ptz/front_end_command/%s/%s", devicecode, channelcode); + if (StrUtil.isNotEmpty(Config.getMonitorAppname())) { + api = String.format("%s/api/ptz/front_end_command/%s/%s", Config.getMonitorAppname(), devicecode, channelcode); + } + logger.info(api); + Map param = new HashMap<>(); + param.put("cmdCode", "130"); + param.put("parameter1", "0"); + param.put("parameter2", poscode); + param.put("combindCode2", "0"); + return sendHttpPost("form", Config.getMonitorIp(), Config.getMonitorPort(), "", api, param, null); + } + + //获取摄像头监控视频截图 + public synchronized String getMonitorVideoSnap(String ffmpegpath, String url, String filename) { + //rtsp://admin:123456@192.168.1.60:554/stream1 可更换到直接拉取摄像机视频流(脱离流媒体服务器的限制) + try { + String imgPath = Config.getSnapFilePath() + filename; + VideoToImageUtil.getVideoPicture(ffmpegpath, url, imgPath); + } catch (Exception e) { + throw new RuntimeException("获取摄像头截图失败"); + } + return filename; + } + + //获取摄像头监控视频截图 + public String getMonitorAudioSnap(String ffmpegpath, String url, String filename) { + //rtsp://admin:123456@192.168.1.60:554/stream1 可更换到直接拉取摄像机视频流(脱离流媒体服务器的限制) + try { + String audioPath = Config.getSnapFilePath() + filename; + VideoToImageUtil.saveCaptureAudioFile(ffmpegpath, url, audioPath); + } catch (Exception e) { + throw new RuntimeException("获取音频文件失败"); + } + return filename; + } + + // 截取10秒钟视频 + public String getMonitorVideo(String ffmpegpath, String url, String filename, String linkageType, + String videoStream) { + String videoPath = Config.getSnapFilePath() + filename; + if ("遥控".equals(linkageType)) { + String dir = StrUtil.subBefore(videoPath, "/", true); + logger.info(dir); + FileUtil.mkdir(dir); + rtspToMP4.StartRecord(videoStream, ffmpegpath, url, videoPath.replace("\\", "/")); + } + if ("遥信".equals(linkageType)) { + rtspToMP4.stopRecord(videoStream); + } + return filename; + } + + //调用智能分析主机图像算法识别 + public JSONObject callPicAnalyse(String requestId, String objectId, String typeList, String customParams, String filename) throws Exception { + String api = "picAnalyse"; + Map param = new HashMap<>(); + param.put("requestHostIp", Config.getPatrolIp()); //巡视主机IP + param.put("requestHostPort", Config.getPatrolPort());//巡视主机端口 + param.put("requestHostAppname", Config.getPatrolAppname());//巡视主机应用名称 + param.put("requestId", requestId);//巡视结果记录ID + JSONArray jsonArray = new JSONArray(); + cn.hutool.json.JSONObject jsonObject2 = JSONUtil.parseObj(customParams); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("objectId", objectId); + jsonObject.put("typeList", JSONUtil.parseArray(typeList)); + jsonObject.put("customParams", jsonObject2); + if (StrUtil.isNotBlank(jsonObject2.getStr("imageNormalUrlPath"))) { + jsonObject.put("imageNormalPath", jsonObject2.getStr("imageNormalUrlPath")); + } else { + jsonObject.put("imageNormalPath", ""); + } + JSONArray imageUrlArray = new JSONArray(); + String methodname = "snapfile"; + if (typeList.indexOf("sound") > 0) { + methodname = "playAudioFile"; + } + // if ("3".equals(Config.getFilefromtype())) { +// imageurl = filename; +// } else { +// imageurl = String.format("http://%s:%s/%s?filename=%s", Config.getPatrolIp(), +// Config.getPatrolPort(), methodname, filename); +// if (StrUtil.isNotEmpty(Config.getPatrolAppname())) { +// imageurl = String.format("http://%s:%s/%s/%s?filename=%s", Config.getPatrolIp(), +// Config.getPatrolPort(), Config.getPatrolAppname(), methodname, filename); +// } +// } + + imageUrlArray.add(filename); + jsonObject.put("imagePathList", imageUrlArray); + jsonArray.add(jsonObject); + param.put("objectList", jsonArray); + logger.info(JSON.toJSONString(jsonArray)); + logger.info("===================调用分析1==============="); + try { + String analyseIp = Config.getAnalyseIp(); + String analysePort = Config.getAnalysePort11(); + List> manufacturerList = algorithmManufacturerConfig.getManufacturer(); + for (Map manufacturer : manufacturerList) { + String taskName = jsonObject2.getStr("taskName"); + if (StrUtil.isBlank(taskName)) { + continue; + } + String name = manufacturer.get("name"); + if (taskName.contains(name)) { + analyseIp = manufacturer.get("ip"); + analysePort = manufacturer.get("port"); + jsonObject2.remove("taskName"); + break; + } + } + cn.hutool.json.JSONObject jsonObject1 = JSONUtil.parseObj(param); + logger.info("===================调用分析===============" + jsonObject1.toString()); + return sendHttpPost("json", analyseIp, analysePort, "", api, param, null); + } catch (Exception e) { + logger.error(e.getMessage());; + logger.error("===================调用分析出错==============="); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("code", "-2"); + return jsonObject1; + } + } + + //调用智能分析主机图像算法识别 + public JSONObject callPicAnalyse(String requestId,String objectId,String typeList,String customParams,String filename,String manId) throws Exception { + String api = "picAnalyse"; + Map param = new HashMap<>(); + param.put("requestHostIp", Config.getPatrolIp()); //巡视主机IP + param.put("requestHostPort", Config.getPatrolPort());//巡视主机端口 + param.put("requestHostAppname", Config.getPatrolAppname());//巡视主机应用名称 + param.put("requestId", requestId);//巡视结果记录ID + JSONArray jsonArray = new JSONArray(); + cn.hutool.json.JSONObject jsonObject2 = JSONUtil.parseObj(customParams); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("objectId", objectId); + jsonObject.put("typeList", JSONUtil.parseArray(typeList)); + jsonObject.put("customParams", jsonObject2); + if (StrUtil.isNotBlank(jsonObject2.getStr("imageNormalUrlPath"))) { + jsonObject.put("imageNormalPath", jsonObject2.getStr("imageNormalUrlPath")); + } else { + jsonObject.put("imageNormalPath", ""); + } + JSONArray imageUrlArray = new JSONArray(); + String methodname = "snapfile"; + if (typeList.indexOf("sound") > 0) { + methodname = "playAudioFile"; + } + // if ("3".equals(Config.getFilefromtype())) { + // imageurl = filename; + // } else { + // imageurl = String.format("http://%s:%s/%s?filename=%s", Config.getPatrolIp(), + // Config.getPatrolPort(), methodname, filename); + // if (StrUtil.isNotEmpty(Config.getPatrolAppname())) { + // imageurl = String.format("http://%s:%s/%s/%s?filename=%s", Config.getPatrolIp(), + // Config.getPatrolPort(), Config.getPatrolAppname(), methodname, filename); + // } + // } + + imageUrlArray.add(filename); + jsonObject.put("imagePathList", imageUrlArray); + jsonArray.add(jsonObject); + param.put("objectList", jsonArray); + logger.info(JSON.toJSONString(jsonArray)); + try { + String analyseIp = Config.getAnalyseIp(); + String analysePort = Config.getAnalysePort11(); + List> manufacturerList = algorithmManufacturerConfig.getManufacturer(); + for (Map manufacturer : manufacturerList) { + String taskName = jsonObject2.getStr("taskName"); + if (StrUtil.isBlank(taskName)) { + continue; + } + String name = manufacturer.get("name"); + if (taskName.contains(name)) { + analyseIp = manufacturer.get("ip"); + analysePort = manufacturer.get("port"); + jsonObject2.remove("taskName"); + break; + } + } + cn.hutool.json.JSONObject jsonObject1 = JSONUtil.parseObj(param); + logger.info("===================调用分析===============" + jsonObject1.toString()); + return sendHttpPost("json", analyseIp, analysePort, "", api, param, null); + } catch (Exception e) { + logger.error(e.getMessage());; + logger.error("===================调用分析出错==============="); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("code", "-2"); + return jsonObject1; + } + } + + public JSONObject callPicAnalyse(String requestId, String objectId, String resImage, String deviceName) throws Exception { + String value = ""; + String conf = "0.9"; + String type = ""; + String pos = ""; + String desc = ""; + Random random = new Random(); + if ("W109".equals(deviceName)) { + // 生成1到3之间的随机数(包括1和3) + int randomNum = random.nextInt(3) + 1; + type = "meter"; + value = "30." + 1; + desc = "表计读取"; + } else if ("W103".equals(deviceName)) { + // 生成1到3之间的随机数(包括1和3) + int randomNum = random.nextInt(3) + 1; + type = "meter"; + value = "25." + 2; + desc = "表计读取"; + } else if ("W105".equals(deviceName)) { + type = "fhz_h"; + value = "1"; + desc = "分合闸_合"; + } else if ("W108".equals(deviceName)) { + // 生成1到3之间的随机数(包括1和3) + int randomNum = random.nextInt(3) + 1; + type = "meter"; + value = "31." + 3; + desc = "表计读取"; + } else if ("W102".equals(deviceName)) { + // 生成1到3之间的随机数(包括1和3) + int randomNum = random.nextInt(3) + 1; + type = "meter"; + value = "30." + 3; + desc = "表计读取"; + } else if ("W110".equals(deviceName)) { + // 生成1到3之间的随机数(包括1和3) + int randomNum = random.nextInt(3) + 1; + type = "meter"; + value = "31." + 9; + desc = "表计读取"; + } + Map map = new HashMap<>(); + map.put("requestId", requestId); + JSONArray resultList = new JSONArray(); + JSONObject resultObject = new JSONObject(); + resultObject.put("objectId", objectId); + JSONArray results = new JSONArray(); + JSONObject result = new JSONObject(); + result.put("type", type); + result.put("value", value); + result.put("code", "2000"); + result.put("resImagePath", resImage); + result.put("pos", new ArrayList<>()); + result.put("conf", conf); + result.put("desc", desc); + results.add(result); + resultObject.put("results", results); + resultList.add(resultObject); + map.put("resultList", resultList); + return sendHttpPost("json", "192.168.1.7", "8082", "", "picAnalyseRetNotify", map, null); + } + + //调用静默监视任务(暂不使用) + public JSONObject callSilentMonitor(String patroldevice_id, String rtspurl, String monitortype, + String customParams) throws Exception { + String api = "/silentMonitor/start"; + Map param = new HashMap<>(); + param.put("requestHostIp", Config.getPatrolIp()); //巡视主机IP + param.put("requestHostPort", Config.getPatrolPort());//巡视主机端口 + param.put("requestHostAppname", Config.getPatrolAppname());//巡视主机应用名称 + param.put("patroldeviceId", patroldevice_id);//摄像机ID + param.put("rtspUrl", rtspurl);//视频流地址 + param.put("monitorType", monitortype);//监控类型 + param.put("customParams", customParams);//定制参数 + try { + return sendHttpPost("json", Config.getAnalyseIp(), Config.getAnalysePort12(), "", api, param, null); + } catch (Exception e) { + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("code", "-2"); + return jsonObject1; + } + } + + //停止静默监视任务(暂不使用) + public JSONObject stopSilentMonitor(String monitor_id) throws Exception { + String api = "/silentMonitor/stop/" + monitor_id; + try { + return sendHttpPost("json", Config.getAnalyseIp(), Config.getAnalysePort12(), "", api, null, null); + } catch (Exception e) { + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("code", "-2"); + return jsonObject1; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/LogFileReader.java b/riis-system/src/main/java/com/yfd/platform/utils/LogFileReader.java new file mode 100644 index 0000000..2a46415 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/LogFileReader.java @@ -0,0 +1,38 @@ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +@Slf4j +public class LogFileReader { + + public static void readLogFile(String filePath) { + try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { + String line; + boolean a = false; + int i = 0; + while ((line = reader.readLine()) != null) { + if (line.contains("任务名称") && line.contains("2024-07-17 23:00:00")) { + a = true; + + } + if (a) { + if (line.contains("开始调用摄像头预置位")) { + i++; + } + // 在这里可以对每一行进行处理 + log.info(line); + } + + } + log.info("=============="+i); + } catch (IOException e) { + log.error(e.getMessage());; + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/MinioExample.java b/riis-system/src/main/java/com/yfd/platform/utils/MinioExample.java new file mode 100644 index 0000000..5c8540a --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/MinioExample.java @@ -0,0 +1,47 @@ +package com.yfd.platform.utils; + +import io.minio.BucketExistsArgs; +import io.minio.GetObjectArgs; +import io.minio.MinioClient; + +import java.io.FileOutputStream; +import java.io.InputStream; + +public class MinioExample { + + public static void main(String[] args) throws Exception { + // 连接到MinIO服务 + String endpoint = "http://192.168.1.7:21001"; + String accessKey = "minioadmin"; + String secretKey = "minioadmin"; + + MinioClient minioClient = MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + + // 确保桶存在 + boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket("cloud-bucket").build()); + if (!isExist) { + throw new RuntimeException("Bucket does not exist"); + } + + // 从这里开始获取图片 + // 获取图片 + String objectName = "your-image-name.jpg"; + try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() + .bucket("your-bucket-name") + .object(objectName) + .build()); + FileOutputStream fos = new FileOutputStream("path/to/save/image.jpg")) { + + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) > 0) { + fos.write(buffer, 0, length); + } + } + + System.out.println("Image downloaded successfully."); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/MpGenerator.java b/riis-system/src/main/java/com/yfd/platform/utils/MpGenerator.java new file mode 100644 index 0000000..2993eb2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/MpGenerator.java @@ -0,0 +1,218 @@ +package com.yfd.platform.utils; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.generator.AutoGenerator; +import com.baomidou.mybatisplus.generator.InjectionConfig; +import com.baomidou.mybatisplus.generator.config.*; +import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; + +import java.util.*; + +@Slf4j +public class MpGenerator { + /** + * 自定义模板,模板引擎是 freemarker + */ + private static final String ENTITY_TEMPLATE_PATH = "/templates/entity.java.ftl"; + private static final String XML_TEMPLATE_PATH = "/templates/mapper.xml.ftl"; + private static final String MAPPER_TEMPLATE_PATH = "/templates/mapper.java.ftl"; + private static final String CONTROLLER_TEMPLATE_PATH = "/templates/controller.java.ftl"; + private static final String SERVICE_IMPL_TEMPLATE_PATH = "/templates/serviceImpl.java.ftl"; + private static final String SERVICE_TEMPLATE_PATH = "/templates/service.java.ftl"; + /** + * 读取控制台内容 + */ + public static String scanner(String tip) { + Scanner scanner = new Scanner(System.in); + StringBuilder help = new StringBuilder(); + help.append("请输入" + tip + ":"); + log.info(help.toString()); + if (scanner.hasNext()) { + String ipt = scanner.next(); + if (!StringUtils.isEmpty(ipt)) { + return ipt; + } + } + throw new MybatisPlusException("请输入正确的" + tip + "!"); + } + + public static void main(String[] args) { + /** + * 代码生成器 + */ + AutoGenerator mpg = new AutoGenerator(); + + //全局配置 + GlobalConfig globalConfig = new GlobalConfig(); + //生成文件的输出目录 + String projectPath = System.getProperty("user.dir"); + + globalConfig.setOutputDir(projectPath + "/src/main/java"); + //Author设置作者 + globalConfig.setAuthor("fwh"); + //开启 Swagger2 注解 + globalConfig.setSwagger2(false); + //是否覆盖文件 + globalConfig.setFileOverride(true); + // 开启 ActiveRecord 模式 + globalConfig.setActiveRecord(true); + // 开启 BaseResultMap(XML ResultMap,生成基本的resultMap) + globalConfig.setBaseResultMap(true); + // 开启 baseColumnList(XML columList,生成基本的SQL片段) + globalConfig.setBaseColumnList(true); + //生成后打开文件 + globalConfig.setOpen(false); + // 自定义文件命名,注意 %s 会自动填充表实体属性!(各层文件名称方式,例如: %Mapper 生成 UserMapper) + globalConfig.setMapperName("%sDao"); + globalConfig.setXmlName("%sMapper"); + globalConfig.setServiceName("%sService"); + globalConfig.setServiceImplName("%sServiceImpl"); + globalConfig.setControllerName("%sController"); + mpg.setGlobalConfig(globalConfig); + + /** + * 数据源配置 + */ + DataSourceConfig dataSourceConfig = new DataSourceConfig(); + // 数据库类型,默认MYSQL + dataSourceConfig.setDbType(DbType.MYSQL); + //自定义数据类型转换 + dataSourceConfig.setTypeConvert(new MySqlTypeConvert()); + dataSourceConfig.setUrl(PropertiesUtils.getPropertyField("spring.datasource.url")); + dataSourceConfig.setDriverName(PropertiesUtils.getPropertyField("spring.datasource.driver-class-name")); + dataSourceConfig.setUsername(PropertiesUtils.getPropertyField("spring.datasource.username")); + dataSourceConfig.setPassword(PropertiesUtils.getPropertyField("spring.datasource.password")); + mpg.setDataSource(dataSourceConfig); + + /** + * 包配置 + */ + PackageConfig packageConfig = new PackageConfig(); + String modileName = scanner("模块名"); + packageConfig.setModuleName(modileName); + String modileName1 = modileName.replace(".","/"); + //父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名 + /** + * 配置文件直接找的dev,包名读取不到. + */ + packageConfig.setParent(PropertiesUtils.getPropertyField("project.package.name")); + //Controller包名 + packageConfig.setController("controller"); + //Entity包名 + packageConfig.setEntity("entity"); + //Mapper包名 + packageConfig.setMapper("dao"); + //Mapper XML包名 + packageConfig.setXml("mapper.xml"); + //Service包名 + packageConfig.setService("service"); + //Service Impl包名 + packageConfig.setServiceImpl("service.impl"); + mpg.setPackageInfo(packageConfig); + /** + * 自定义配置 + */ + InjectionConfig injectionConfig = new InjectionConfig() { + @Override + public void initMap() { + Map map = new HashMap(); + map.put("Author", "Author : " + this.getConfig().getGlobalConfig().getAuthor()); + this.setMap(map); + } + }; + /** + * 自定义输出配置 + */ + List focList = new ArrayList<>(); + focList.add(new FileOutConfig(XML_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // mapper.xml生成路径+名称 如果Entity设置了前后缀、此处注意xml的名称会跟着发生变化 + return projectPath + "/src/main/resources/mapper/"+modileName1+"/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; + } + + }); + //调整 Entity 生成目录 + focList.add(new FileOutConfig(ENTITY_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // Entity生成路径+名称 如果Entity设置了前后缀、此处名称会跟着发生变化 + return projectPath + "/src/main/java/com/yfd/platform/modules/domain"+modileName1+"/entity/" + tableInfo.getEntityName() + StringPool.DOT_JAVA; + } + }); + //调整 Mapper 生成目录 + focList.add(new FileOutConfig(MAPPER_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // Mapper生成路径+名称 如果Entity设置了前后缀、此处注意Mapper的名称会跟着发生变化 + return projectPath + "/src/main/java/com/yfd/platform/modules/domain/"+modileName1+"/dao/" + tableInfo.getEntityName() + "Dao" + StringPool.DOT_JAVA; + } + }); + //调整Controller生成 + focList.add(new FileOutConfig(CONTROLLER_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // Controller生成路径+名称 如果Entity设置了前后缀、此处注意Controller的名称会跟着发生变化 + return projectPath + "/src/main/java/com/yfd/platform/modules/domain"+modileName1+"/controller/" + tableInfo.getEntityName() + "Controller" + StringPool.DOT_JAVA; + } + }); + //调整Service生成 + focList.add(new FileOutConfig(SERVICE_TEMPLATE_PATH) { + @Override + public String outputFile(TableInfo tableInfo) { + // Service生成路径+名称 如果Entity设置了前后缀、此处注意Service的名称会跟着发生变化 + return projectPath + "/src/main/java/com/yfd/platform/modules/domain"+modileName1+"/service/" + tableInfo.getEntityName() + "Service" + StringPool.DOT_JAVA; + } + }); + + injectionConfig.setFileOutConfigList(focList); + mpg.setCfg(injectionConfig); + + /** + * 配置模板 + */ + TemplateConfig templateConfig = new TemplateConfig(); + templateConfig.setXml(null); + mpg.setTemplate(templateConfig); + + /** + * 策略配置 + */ + StrategyConfig strategy = new StrategyConfig(); + // 数据库表映射到实体的命名策略(表名生成策略) + strategy.setNaming(NamingStrategy.underline_to_camel); + strategy.setColumnNaming(NamingStrategy.underline_to_camel); + strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); + //实体是否为lombok模型(默认 false) + strategy.setEntityLombokModel(false); + //生成 @RestController 控制器 + strategy.setRestControllerStyle(true); + //设置自定义继承的Service类全称,带包名 + //strategy.setSuperServiceClass(); + //strategy.setSuperServiceImplClass(); + //设置自定义继承的Entity类全称,带包名 + strategy.setSuperEntityClass("BaseEntity"); + //设置自定义继承的Controller类全称,带包名 + strategy.setSuperControllerClass("BaseController"); + //设置自定义继承的Mapper类全称,带包名 + //strategy.setSuperMapperClass(); + //设置自定义基础的Entity类,公共字段 + //strategy.setSuperEntityColumns("id"); + //驼峰转连字符 + strategy.setControllerMappingHyphenStyle(false); + //表名前缀 + // strategy.setTablePrefix(packageConfig.getModuleName() + "_"); + + mpg.setStrategy(strategy); + mpg.setTemplateEngine(new FreemarkerTemplateEngine()); + mpg.execute(); + } +} + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/MySQLBackupRestore.java b/riis-system/src/main/java/com/yfd/platform/utils/MySQLBackupRestore.java new file mode 100644 index 0000000..e9523dd --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/MySQLBackupRestore.java @@ -0,0 +1,61 @@ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +@Slf4j +public class MySQLBackupRestore { + + private final String host; + private final String port; + private final String user; + private final String password; + private final String database; + + public MySQLBackupRestore(String host, String port, String user, String password, String database) { + this.host = host; + this.port = port; + this.user = user; + this.password = password; + this.database = database; + } + + public void backup(String backupFile) throws IOException, InterruptedException { + String cmd = String.format("mysqldump -h%s -P%s -u%s -p%s %s > %s", + host, port, user, password, database, backupFile); + + Process process = Runtime.getRuntime().exec(new String[]{"bash", "-c", cmd}); + int processComplete = process.waitFor(); + + if (processComplete == 0) { + log.info("Backup completed successfully."); + } else { + log.info("Backup failed."); + } + } + + public void restore(String backupFile) throws IOException, InterruptedException { + String[] executeCmd = new String[]{"mysql", database, "-u" + user, "-p" + password, "-e", "source " + backupFile}; + + Process runtimeProcess = Runtime.getRuntime().exec(executeCmd); + int processComplete = runtimeProcess.waitFor(); + + if (processComplete == 0) { + log.info("Database restored successfully"); + } else { + log.error("Could not restore the database"); + } + } + + public static void main(String[] args) { + MySQLBackupRestore backupRestore = new MySQLBackupRestore("localhost", "3306", "root", "123456", "of_cd1"); + + try { + backupRestore.backup("backup.sql"); + //backupRestore.restore("D:/backup.sql"); + } catch (IOException | InterruptedException e) { + log.error(e.getMessage());; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/MyTrustManager.java b/riis-system/src/main/java/com/yfd/platform/utils/MyTrustManager.java new file mode 100644 index 0000000..9e9c039 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/MyTrustManager.java @@ -0,0 +1,22 @@ +package com.yfd.platform.utils; + +import org.springframework.stereotype.Component; + +import javax.net.ssl.X509TrustManager; +import java.security.cert.X509Certificate; + +@Component +public class MyTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } +} \ No newline at end of file diff --git a/riis-system/src/main/java/com/yfd/platform/utils/MybatisUtil.java b/riis-system/src/main/java/com/yfd/platform/utils/MybatisUtil.java new file mode 100644 index 0000000..109d605 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/MybatisUtil.java @@ -0,0 +1,144 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.StrUtil; +import org.apache.ibatis.mapping.*; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.SystemMetaObject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/****************************** + * 用途说明: 特殊字符处理 + * 作者姓名: tw + * 创建时间: 2023/11/09 10:50 + ******************************/ +public class MybatisUtil { + + + /** + * 检查sql中,是否含有like查询 + */ + public static final Pattern LIKE_PARAM_PATTERN = + Pattern.compile("like\\s(concat)?[\\w'\"\\(\\)\\%,\\s\\n\\t]*\\?", Pattern.CASE_INSENSITIVE); + + public static void escapeParameterIfContainingLike(MappedStatement ms, BoundSql boundSql) { + final SqlCommandType sqlCommandType = ms.getSqlCommandType(); + final StatementType statementType = ms.getStatementType(); + // 只处理 有参数的查询语句 + if (SqlCommandType.SELECT == sqlCommandType && StatementType.PREPARED == statementType) { + escapeParameterIfContainingLike(boundSql); + } + } + + public static void escapeParameterIfContainingLike(BoundSql boundSql) { + if (null == boundSql) { + return; + } + final String prepareSql = boundSql.getSql(); + final List parameterMappings = boundSql.getParameterMappings(); + + // 找到 like 后面的参数 + final List positions = findLikeParam(prepareSql); + if (positions.isEmpty()) { + return; + } + + final List likeParameterMappings = new ArrayList<>(parameterMappings.size()); + // 复制 + final MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject()); + for (ParameterMapping parameterMapping : parameterMappings) { + final String property = parameterMapping.getProperty(); + if (!metaObject.hasGetter(property)) { + continue; + } + boundSql.setAdditionalParameter(property, metaObject.getValue(property)); + } + + for (Integer position : positions) { + final ParameterMapping parameterMapping = parameterMappings.get(position); + likeParameterMappings.add(parameterMapping); + } + // 覆盖 转译字符 + delegateMetaParameterForEscape(boundSql, likeParameterMappings); + } + + /** + * @param boundSql 原BoundSql + * @param likeParameterMappings 需要转义的参数 + */ + private static void delegateMetaParameterForEscape(BoundSql boundSql, + List likeParameterMappings) { + final MetaObject metaObject = SystemMetaObject.forObject(boundSql.getParameterObject()); + for (ParameterMapping mapping : likeParameterMappings) { + final String property = mapping.getProperty(); + if (!metaObject.hasGetter(property)) { + continue; + } + final Object value = metaObject.getValue(property); + boolean flag = false; + if (value instanceof String) { + String newValue = (String) value; + if (property.contains("paramNameValuePairs")) { + newValue = StrUtil.replaceLast(StrUtil.replaceFirst(newValue, "%", ""), "%", ""); + flag = true; + } + final String saveValue = convertToSqlSafeValue(newValue, flag); + boundSql.setAdditionalParameter(property, saveValue); + } + } + } + + /** + * 匹配like 位置, 如 + * like concat('%',?,'%') + * like CONCAT('%',?,'%') + * LIKE CONCAT('%', ?,'%') + * lIKE conCAT('%', ?,'%') + * like ? + */ + private static List findLikeParam(String prepareSql) { + if (CharSequenceUtil.isBlank(prepareSql)) { + return Collections.emptyList(); + } + final Matcher matcher = LIKE_PARAM_PATTERN.matcher(prepareSql); + if (!matcher.find()) { + return Collections.emptyList(); + } + + matcher.reset(); + int pos = 0; + final List indexes = new ArrayList<>(); + while (matcher.find(pos)) { + final int start = matcher.start(); + final int index = CharSequenceUtil.count(prepareSql.substring(0, start), '?'); + indexes.add(index); + pos = matcher.end(); + } + return indexes; + } + + /** + * 转译 \ % _ + * 禁止与escape 同时使用 + */ + public static String convertToSqlSafeValue(String str, boolean flag) { + if (CharSequenceUtil.isEmpty(str)) { + return str; + } + String reg = str.replace("\\", "\\\\") + .replace("%", "\\%") + .replace("_", "\\_"); + if (flag) { + String replace = reg; + reg = "%" + replace + "%"; + } + return reg; + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/OSInfoUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/OSInfoUtils.java new file mode 100644 index 0000000..d37c13f --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/OSInfoUtils.java @@ -0,0 +1,148 @@ +package com.yfd.platform.utils; + + +public class OSInfoUtils { + + private static String Any = "any"; + private static String Linux = "Linux"; + private static String Mac_OS = "Mac OS"; + private static String Mac_OS_X = "Mac OS X"; + private static String Windows = "Windows"; + private static String OS2 = "OS/2"; + private static String Solaris = "Solaris"; + private static String SunOS = "SunOS"; + private static String MPEiX = "MPE/iX"; + private static String HP_UX = "HP-UX"; + private static String AIX = "AIX"; + private static String OS390 = "OS/390"; + private static String FreeBSD = "FreeBSD"; + private static String Irix = "Irix"; + private static String Digital_Unix = "Digital Unix"; + private static String NetWare_411 = "NetWare"; + private static String OSF1 = "OSF1"; + private static String OpenVMS = "OpenVMS"; + private static String Others = "Others"; + + private static String OS = System.getProperty("os.name").toLowerCase(); + + private static OSInfoUtils _instance = new OSInfoUtils(); + + private String platform; + + private OSInfoUtils(){} + + public static boolean isLinux(){ + return OS.indexOf("linux")>=0; + } + + public static boolean isMacOS(){ + return OS.indexOf("mac")>=0&&OS.indexOf("os")>0&&OS.indexOf("x")<0; + } + + public static boolean isMacOSX(){ + return OS.indexOf("mac")>=0&&OS.indexOf("os")>0&&OS.indexOf("x")>0; + } + + public static boolean isWindows(){ + return OS.indexOf("windows")>=0; + } + + public static boolean isOS2(){ + return OS.indexOf("os/2")>=0; + } + + public static boolean isSolaris(){ + return OS.indexOf("solaris")>=0; + } + + public static boolean isSunOS(){ + return OS.indexOf("sunos")>=0; + } + + public static boolean isMPEiX(){ + return OS.indexOf("mpe/ix")>=0; + } + + public static boolean isHPUX(){ + return OS.indexOf("hp-ux")>=0; + } + + public static boolean isAix(){ + return OS.indexOf("aix")>=0; + } + + public static boolean isOS390(){ + return OS.indexOf("os/390")>=0; + } + + public static boolean isFreeBSD(){ + return OS.indexOf("freebsd")>=0; + } + + public static boolean isIrix(){ + return OS.indexOf("irix")>=0; + } + + public static boolean isDigitalUnix(){ + return OS.indexOf("digital")>=0&&OS.indexOf("unix")>0; + } + + public static boolean isNetWare(){ + return OS.indexOf("netware")>=0; + } + + public static boolean isOSF1(){ + return OS.indexOf("osf1")>=0; + } + + public static boolean isOpenVMS(){ + return OS.indexOf("openvms")>=0; + } + + /** + * 获取操作系统名字 + * @return 操作系统名 + */ + public static String getOSname(){ + if(isAix()){ + _instance.platform = AIX; + }else if (isDigitalUnix()) { + _instance.platform = Digital_Unix; + }else if (isFreeBSD()) { + _instance.platform = FreeBSD; + }else if (isHPUX()) { + _instance.platform = HP_UX; + }else if (isIrix()) { + _instance.platform = Irix; + }else if (isLinux()) { + _instance.platform = Linux; + }else if (isMacOS()) { + _instance.platform = Mac_OS; + }else if (isMacOSX()) { + _instance.platform = Mac_OS_X; + }else if (isMPEiX()) { + _instance.platform = MPEiX; + }else if (isNetWare()) { + _instance.platform = NetWare_411; + }else if (isOpenVMS()) { + _instance.platform = OpenVMS; + }else if (isOS2()) { + _instance.platform = OS2; + }else if (isOS390()) { + _instance.platform = OS390; + }else if (isOSF1()) { + _instance.platform = OSF1; + }else if (isSolaris()) { + _instance.platform = Solaris; + }else if (isSunOS()) { + _instance.platform = SunOS; + }else if (isWindows()) { + _instance.platform = Windows; + }else{ + _instance.platform = Others; + } + return _instance.platform; + } + +} + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/PropertiesUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/PropertiesUtils.java new file mode 100644 index 0000000..93c7cfd --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/PropertiesUtils.java @@ -0,0 +1,29 @@ +package com.yfd.platform.utils; + +import org.springframework.core.io.ClassPathResource; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Properties; +/****************************** + * 用途说明: + * 作者姓名: pcj + * 创建时间: 2022/9/20 14:31 + ******************************/ +public class PropertiesUtils { + public final static String RESOURCE_PATH = "application.properties"; + + public final static Properties properties = new Properties(); + + public static String getPropertyField(String parameter) { + //对应resources目录下的资源路径 + ClassPathResource resource = new ClassPathResource(RESOURCE_PATH); + try { + properties.load(new InputStreamReader(resource.getInputStream(), "gbk")); + } catch (IOException e) { + throw new RuntimeException(e); + } + return properties.getProperty(parameter); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/QuartzManage.java b/riis-system/src/main/java/com/yfd/platform/utils/QuartzManage.java new file mode 100644 index 0000000..9634bf7 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/QuartzManage.java @@ -0,0 +1,188 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import com.yfd.platform.system.domain.QuartzJob; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Date; + +import static org.quartz.TriggerBuilder.newTrigger; + +/** + * + * @date 2019-01-07 + */ +@Slf4j +@Component +public class QuartzManage { + + private static final String JOB_NAME = "TASK_"; + + @Resource(name = "scheduler") + private Scheduler scheduler; + + public void addJob(QuartzJob quartzJob) { + try { + // 构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). + withIdentity(JOB_NAME + quartzJob.getId()).build(); + + //通过触发器名和cron 表达式创建 Trigger + Trigger cronTrigger = newTrigger() + .withIdentity(JOB_NAME + quartzJob.getId()) + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getJobCron())) + .build(); + + cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); + + //重置启动时间 + ((CronTriggerImpl) cronTrigger).setStartTime(new Date()); + + //执行定时任务 + scheduler.scheduleJob(jobDetail, cronTrigger); + + + // 暂停任务 + if ("0".equals(quartzJob.getStatus())) { + pauseJob(quartzJob); + } + } catch (Exception e) { + log.error("创建定时任务失败", e); + throw new RuntimeException("创建定时任务失败"); + } + } + + /** + * 更新job cron表达式 + * + * @param quartzJob / + */ + public void updateJobCron(QuartzJob quartzJob) { + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = + (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(quartzJob); + trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + } + CronScheduleBuilder scheduleBuilder = + CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()); + trigger = + trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + //重置启动时间 + ((CronTriggerImpl) trigger).setStartTime(new Date()); + trigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); + + scheduler.rescheduleJob(triggerKey, trigger); + // 暂停任务 + if ("0".equals(quartzJob.getStatus())) { + pauseJob(quartzJob); + } + } catch (Exception e) { + log.error("更新定时任务失败", e); + throw new RuntimeException("更新定时任务失败"); + } + + } + + /** + * 删除一个job + * + * @param quartzJob / + */ + public void deleteJob(QuartzJob quartzJob) { + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + scheduler.deleteJob(jobKey); + } catch (Exception e) { + log.error("删除定时任务失败", e); + throw new RuntimeException("删除定时任务失败"); + } + } + + /** + * 恢复一个job + * + * @param quartzJob / + */ + public void resumeJob(QuartzJob quartzJob) { + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = + (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(quartzJob); + } + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.resumeJob(jobKey); + } catch (Exception e) { + log.error("恢复定时任务失败", e); + throw new RuntimeException("恢复定时任务失败"); + } + } + + /** + * 立即执行job + * + * @param quartzJob / + */ + public void runJobNow(QuartzJob quartzJob) { + try { + TriggerKey triggerKey = + TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); + CronTrigger trigger = + (CronTrigger) scheduler.getTrigger(triggerKey); + // 如果不存在则创建一个定时任务 + if (trigger == null) { + addJob(quartzJob); + } + JobDataMap dataMap = new JobDataMap(); + dataMap.put(QuartzJob.JOB_KEY, quartzJob); + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.triggerJob(jobKey, dataMap); + } catch (Exception e) { + log.error("定时任务执行失败", e); + throw new RuntimeException("定时任务执行失败"); + } + } + + /** + * 暂停一个job + * + * @param quartzJob / + */ + public void pauseJob(QuartzJob quartzJob) { + try { + JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + scheduler.pauseJob(jobKey); + } catch (Exception e) { + log.error("定时任务暂停失败", e); + throw new RuntimeException("定时任务暂停失败"); + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/QuartzRunnable.java b/riis-system/src/main/java/com/yfd/platform/utils/QuartzRunnable.java new file mode 100644 index 0000000..3a225c6 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/QuartzRunnable.java @@ -0,0 +1,57 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.concurrent.Callable; + +/** + * 执行定时任务 + */ +@Slf4j +public class QuartzRunnable implements Callable { + + private final Object target; + private final Method method; + private final String params; + + QuartzRunnable(String beanName, String methodName, String params) + throws NoSuchMethodException, SecurityException { + this.target = SpringContextHolder.getBean(beanName); + this.params = params; + + if (StringUtils.isNotBlank(params)) { + this.method = target.getClass().getDeclaredMethod(methodName, String.class); + } else { + this.method = target.getClass().getDeclaredMethod(methodName); + } + } + + @Override + public Object call() throws Exception { + ReflectionUtils.makeAccessible(method); + if (StringUtils.isNotBlank(params)) { + method.invoke(target, params); + } else { + method.invoke(target); + } + return null; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/RTPStreamer.java b/riis-system/src/main/java/com/yfd/platform/utils/RTPStreamer.java new file mode 100644 index 0000000..5e52921 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/RTPStreamer.java @@ -0,0 +1,102 @@ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +@Slf4j +public class RTPStreamer { + + // 配置参数 + private static final int RTP_PAYLOAD_TYPE = 100; // PT字段,动态类型96 + private static final int RTP_HEADER_SIZE = 12; // RTP头部固定大小 + private static final int MTU = 1000; // 最大传输单元 + private static final int SAMPLE_RATE = 90000; // 时间戳的采样率 + private static final int FRAME_RATE = 30; // 视频帧率 + private static final long TIMESTAMP_INCREMENT = SAMPLE_RATE / FRAME_RATE; // 时间戳增量 + + public static void streamVideo(String videoPath, String destinationIp, int destinationPort) throws Exception { + // 初始化RTP传输参数 + DatagramSocket socket = new DatagramSocket(); + InetAddress address = InetAddress.getByName(destinationIp); + int sequenceNumber = 0; + long timestamp = 0; + int ssrc = (int) (Math.random() * Integer.MAX_VALUE); + + // 构造FFmpeg命令 + String ffmpegCommand = String.format("ffmpeg -i %s -an -vcodec libx264 -bsf:v h264_mp4toannexb -f h264 -", videoPath); + + // 启动FFmpeg进程 + Process process = Runtime.getRuntime().exec(ffmpegCommand); + InputStream inputStream = process.getInputStream(); + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + + // 读取FFmpeg输出(H.264编码数据) + byte[] buffer = new byte[MTU]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + // 将读取的 H.264 数据封装成 RTP 包 + byte[] rtpPacket = createRTPPacket(buffer, bytesRead, sequenceNumber++, timestamp, ssrc); + timestamp += TIMESTAMP_INCREMENT; + + // 发送 RTP 包 + DatagramPacket packet = new DatagramPacket(rtpPacket, rtpPacket.length, address, destinationPort); + socket.send(packet); + + // 打印日志 + log.info("Sent RTP packet, sequence: %d, timestamp: %d, size: %d%n", sequenceNumber, timestamp, bytesRead); + } + + // 关闭资源 + socket.close(); + process.destroy(); + + // 打印 FFmpeg 错误流(如果有) + String line; + while ((line = errorReader.readLine()) != null) { + log.error(line); + } + } + + // 构造 RTP 包 + private static byte[] createRTPPacket(byte[] payload, int payloadSize, int sequenceNumber, long timestamp, int ssrc) { + // RTP头部固定为12字节 + byte[] rtpPacket = new byte[RTP_HEADER_SIZE + payloadSize]; + rtpPacket[0] = (byte) (2 << 6); // Version = 2 + rtpPacket[1] = (byte) RTP_PAYLOAD_TYPE; // PT字段 + rtpPacket[2] = (byte) (sequenceNumber >> 8); // 序列号高字节 + rtpPacket[3] = (byte) (sequenceNumber & 0xFF); // 序列号低字节 + rtpPacket[4] = (byte) (timestamp >> 24); // 时间戳高字节 + rtpPacket[5] = (byte) (timestamp >> 16); + rtpPacket[6] = (byte) (timestamp >> 8); + rtpPacket[7] = (byte) (timestamp & 0xFF); // 时间戳低字节 + rtpPacket[8] = (byte) (ssrc >> 24); // SSRC高字节 + rtpPacket[9] = (byte) (ssrc >> 16); + rtpPacket[10] = (byte) (ssrc >> 8); + rtpPacket[11] = (byte) (ssrc & 0xFF); // SSRC低字节 + + // 复制负载数据 + System.arraycopy(payload, 0, rtpPacket, RTP_HEADER_SIZE, payloadSize); + + return rtpPacket; + } + + public static void main(String[] args) { + try { + // 输入 MP4 文件路径、目标 IP 和端口 + String videoPath = "D:\\ffmpeg\\bin\\encoded_h264.h264"; + String destinationIp = "192.168.22.237"; + int destinationPort = 5083; + + // 启动视频流 + streamVideo(videoPath, destinationIp, destinationPort); + } catch (Exception e) { + log.error(e.getMessage());; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/RequestHolder.java b/riis-system/src/main/java/com/yfd/platform/utils/RequestHolder.java new file mode 100644 index 0000000..f24d277 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/RequestHolder.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Objects; + +/** + * 获取 HttpServletRequest + * + * @date 2018-11-24 + */ +public class RequestHolder { + + public static HttpServletRequest getHttpServletRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/RtspToMP4.java b/riis-system/src/main/java/com/yfd/platform/utils/RtspToMP4.java new file mode 100644 index 0000000..4a58570 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/RtspToMP4.java @@ -0,0 +1,221 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.modules.basedata.domain.LinkageSignal; +import com.yfd.platform.modules.basedata.mapper.LinkageSignalMapper; +import com.yfd.platform.modules.patroltask.domain.Task; +import com.yfd.platform.modules.patroltask.domain.TaskResult; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import com.yfd.platform.modules.patroltask.mapper.TaskMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskResultMapper; +import com.yfd.platform.modules.patroltask.mapper.TaskTodoMapper; +import com.yfd.platform.modules.patroltask.service.ITaskTodoService; +import com.yfd.platform.modules.robotapi.service.IStationRobotService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * TODO: + * + * @Author: ZHANG + * @create: 2021/8/27 16:11 + */ +@Component +@Slf4j +public class RtspToMP4 { + + private Map map = new ConcurrentHashMap<>(); + + @Resource + private TaskResultMapper taskResultMapper; + @Resource + private TaskTodoMapper taskTodoMapper; + @Resource + private LinkageSignalMapper linkageSignalMapper; + @Resource + private TaskMapper taskMapper; + + @Value("${file-space.confirmfilepath}") + private String confirmfilepath; + + public void createVideocfmresult(String resultId) { + + TaskResult taskResult = taskResultMapper.selectById(resultId); + taskResult.setValue("1"); + taskResult.setDesc("合位"); + taskResult.setDefectFilePath(taskResult.getFilePath()); + TaskTodo taskTodo = taskTodoMapper.selectById(taskResult.getTaskTodoId()); + if (taskTodo.getTaskType().equals("4")) { + String controlNum = taskTodo.getControlNum(); + List linkageSignals = + linkageSignalMapper.selectList(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, controlNum)); + if (linkageSignals.size() > 0) { + LinkageSignal linkageSignal = linkageSignals.get(0); + String linkageType = linkageSignal.getLinkageType(); + if ("1".equals(linkageType)) { + //对于一键顺控任务调用顺控文件生成和处理方法 + try { + this.createVideo(taskResult.getStationCode(), taskResult.getTaskCode(), + taskResult.getDesc()); + } catch (Exception e) { + log.error(e.getMessage()); + } + + } + } + + } + + } + + private void createVideo(String stationcode, String taskcode, String videoresult) { + String[] temp = taskcode.split("-"); + //监控索引号 + String monitorindex = temp[0]; + //事件时标 + String eventtime = DateUtil.format(DateUtil.date(Long.parseLong(temp[1])), "yyyy-MM-dd HH:mm:ss.SSS"); + + List> tasklist = + taskMapper.selectMaps(new LambdaQueryWrapper().eq(Task::getTaskCode, taskcode)); + // String value1 = "";//一键顺控的原始值 + String equip = ""; + List linkageSignals = + linkageSignalMapper.selectList(new LambdaQueryWrapper().eq(LinkageSignal::getControlNum, monitorindex)); + String stationNum = ""; + if (tasklist.size() > 0) {//任务表 + // value1 = tasklist.get(0).get("custom3").toString(); + if (linkageSignals.size() > 0) { + LinkageSignal linkageSignal = linkageSignals.get(0); + stationNum = linkageSignal.getStationNum(); + String linkageDeviceName = linkageSignal.getLinkageDeviceName(); + equip = StrUtil.subBefore(linkageDeviceName, "-", true); + } + } + // String result = "分析识别"; + // if (value1.equals("合位") && videoresult.equals("刀闸关闭")) { + // result = "合闸正常"; + // } + // if (value1.equals("合位") && (videoresult.equals("中间状态") || videoresult.equals("刀闸打开"))) { + // result = "合闸异常"; + // } + // if (value1.equals("分位") && videoresult.equals("刀闸打开")) { + // result = "分闸正常"; + // } + // if (value1.equals("分位") && (videoresult.equals("中间状态") || videoresult.equals("刀闸关闭"))) { + // result = "分闸异常"; + // } + String cimfilecontent = getReturnCimFile(stationNum, monitorindex, equip, videoresult, eventtime); + String filename = confirmfilepath + "videocfmresult.cime"; + FileUtil.writeString(cimfilecontent, filename, Charset.forName("GB2312")); + } + + private String getReturnCimFile(String stationcode, String monitorindex, String equip, String result, String time) { + StringBuffer content = new StringBuffer(); + content.append(String.format("\n", DateUtil.now())); + content.append("\n"); + content.append("@序号 站序号 监控索引号 设备名称 设备状态 事件时标\n"); + String resultinfo = String.format("#1\t%s\t%s\t%s\t%s\t%s\n", + stationcode, monitorindex, equip, result, time); + content.append(resultinfo); + content.append("\n"); + return content.toString(); + } + + public static class In implements Runnable { + + private InputStream inputStream; + + public In(InputStream inputStream) { + this.inputStream = inputStream; + } + + @Override + public void run() { + try { + //转成字符输入流 + InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "gbk"); + int len = -1; + char[] c = new char[1024]; + //读取进程输入流中的内容 + while ((len = inputStreamReader.read(c)) != -1) { + String s = new String(c, 0, len); + log.info(s); + } + } catch (Exception e) { + log.error(e.getMessage()); + } + } + } + + public Process StartRecord(String videoStream, String ffmpegPath, String streamUrl, String FilePath) { + ProcessBuilder processBuilder = new ProcessBuilder(); + //定义命令内容 + List command = new ArrayList<>(); + command.add(ffmpegPath + "ffmpeg"); + command.add("-rtsp_transport"); + command.add("tcp"); + command.add("-i"); + command.add(streamUrl); + command.add("-t"); + command.add("30"); + command.add("-f"); + command.add("mp4"); + command.add(FilePath); + processBuilder.command(command); + log.info("脚本:" + command.toString()); + //将标准输入流和错误输入流合并,通过标准输入流读取信息 + processBuilder.redirectErrorStream(true); + try { + //启动进程 + Process process = processBuilder.start(); + log.info("开始时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()))); + //获取输入流 + InputStream inputStream = process.getInputStream(); + Thread inThread = new Thread(new In(inputStream)); + inThread.start(); + map.put(videoStream, process); + int i = process.waitFor(); + log.info("StartRecord结束" + i); + return process; + } catch (Exception e) { + log.error(e.getMessage()); + } + + return null; + } + + public boolean stopRecord(String videoStream) { + try { + if (map.containsKey(videoStream)) { + Process process = map.get(videoStream); + if (process == null) { + return false; + } + OutputStream os = process.getOutputStream(); + os.write("q".getBytes()); + // 一定要刷新 + os.flush(); + os.close(); + } + } catch (Exception err) { + log.error(err.getMessage()); + return false; + } + return true; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/SecurityUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/SecurityUtils.java new file mode 100644 index 0000000..d04a82d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/SecurityUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.exception.BadRequestException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; + +import java.util.List; + +/** + * 获取当前登录的用户 + * + * @date 2019-01-17 + */ +@Slf4j +public class SecurityUtils { + + /** + * 获取当前登录的用户 + * @return UserDetails + */ + public static UserDetails getCurrentUser() { + UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class); + return userDetailsService.loadUserByUsername(getCurrentUsername()); + } + + /** + * 获取系统用户名称 + * + * @return 系统用户名称 + */ + public static String getCurrentUsername() { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期"); + } + if (authentication.getPrincipal() instanceof UserDetails) { + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + return userDetails.getUsername(); + } + throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息"); + } + + /** + * 获取系统用户ID + * @return 系统用户ID + */ + public static Long getCurrentUserId() { + UserDetails userDetails = getCurrentUser(); + return new JSONObject(new JSONObject(userDetails).get("user")).get("id", Long.class); + } + + /** + * 获取当前用户的数据权限 + * @return / + */ + public static List getCurrentUserDataScope(){ + UserDetails userDetails = getCurrentUser(); + JSONArray array = JSONUtil.parseArray(new JSONObject(userDetails).get("dataScopes")); + return JSONUtil.toList(array,Long.class); + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/SpringContextHolder.java b/riis-system/src/main/java/com/yfd/platform/utils/SpringContextHolder.java new file mode 100644 index 0000000..2d18938 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/SpringContextHolder.java @@ -0,0 +1,144 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.env.Environment; + +import java.util.ArrayList; +import java.util.List; +/** + * @date 2019-01-07 + */ +@Slf4j +public class SpringContextHolder implements ApplicationContextAware, DisposableBean { + + private static ApplicationContext applicationContext = null; + private static final List CALL_BACKS = new ArrayList<>(); + private static boolean addCallback = true; + + /** + * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。 + * 在SpringContextHolder 初始化后,进行回调使用 + * + * @param callBack 回调函数 + */ + public synchronized static void addCallBacks(CallBack callBack) { + if (addCallback) { + SpringContextHolder.CALL_BACKS.add(callBack); + } else { + log.warn("CallBack:{} 已无法添加!立即执行", callBack.getCallBackName()); + callBack.executor(); + } + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + assertContextInjected(); + return (T) applicationContext.getBean(name); + } + + /** + * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型. + */ + public static T getBean(Class requiredType) { + assertContextInjected(); + return applicationContext.getBean(requiredType); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param defaultValue 默认值 + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, T defaultValue, Class requiredType) { + T result = defaultValue; + try { + result = getBean(Environment.class).getProperty(property, requiredType); + } catch (Exception ignored) {} + return result; + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @return / + */ + public static String getProperties(String property) { + return getProperties(property, null, String.class); + } + + /** + * 获取SpringBoot 配置信息 + * + * @param property 属性key + * @param requiredType 返回类型 + * @return / + */ + public static T getProperties(String property, Class requiredType) { + return getProperties(property, null, requiredType); + } + + /** + * 检查ApplicationContext不为空. + */ + private static void assertContextInjected() { + if (applicationContext == null) { + throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" + + ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder."); + } + } + + /** + * 清除SpringContextHolder中的ApplicationContext为Null. + */ + private static void clearHolder() { + log.debug("清除SpringContextHolder中的ApplicationContext:" + + applicationContext); + applicationContext = null; + } + + @Override + public void destroy() { + SpringContextHolder.clearHolder(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringContextHolder.applicationContext != null) { + log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext); + } + SpringContextHolder.applicationContext = applicationContext; + if (addCallback) { + for (CallBack callBack : SpringContextHolder.CALL_BACKS) { + callBack.executor(); + } + CALL_BACKS.clear(); + } + SpringContextHolder.addCallback = false; + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/SpringUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/SpringUtils.java new file mode 100644 index 0000000..d46cf2e --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/SpringUtils.java @@ -0,0 +1,145 @@ +package com.yfd.platform.utils; + + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + + public static Map getBeansByAnnotation(Class clsName) throws BeansException{ + + return beanFactory.getBeansWithAnnotation(clsName); + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/StringUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/StringUtils.java new file mode 100644 index 0000000..09b4ba8 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/StringUtils.java @@ -0,0 +1,324 @@ +/* + * Copyright 2019-2020 Zheng Jie + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yfd.platform.utils; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.constant.Constant; +import eu.bitwalker.useragentutils.Browser; +import eu.bitwalker.useragentutils.UserAgent; +import lombok.SneakyThrows; +import org.lionsoul.ip2region.DataBlock; +import org.lionsoul.ip2region.DbConfig; +import org.lionsoul.ip2region.DbSearcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Calendar; +import java.util.Date; +import java.util.Enumeration; + +/** + * + * 字符串工具类, 继承org.apache.commons.lang3.StringUtils类 + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils { + + private static final Logger log = LoggerFactory.getLogger(StringUtils.class); + private static boolean ipLocal = false; + private static File file ; + private static DbConfig config; + private static final char SEPARATOR = '_'; + private static final String UNKNOWN = "unknown"; + + static { + SpringContextHolder.addCallBacks(() -> { + StringUtils.ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class); + if (ipLocal) { + /* + * 此文件为独享 ,不必关闭 + */ + String path = "ip2region/ip2region.db"; + String name = "ip2region.db"; + try { + config = new DbConfig(); + file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + }); + } + + /********************************** + * 用途说明: 获取浏览器真正的ip + * 参数说明 request + * 返回值说明: java.lang.String + ***********************************/ + public static String getIpAddr(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCamelCase(String s) { + if (s == null) { + return null; + } + + s = s.toLowerCase(); + + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + if (c == SEPARATOR) { + upperCase = true; + } else if (upperCase) { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + public static String toCapitalizeCamelCase(String s) { + if (s == null) { + return null; + } + s = toCamelCase(s); + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * 驼峰命名法工具 + * + * @return toCamelCase(" hello_world ") == "helloWorld" + * toCapitalizeCamelCase("hello_world") == "HelloWorld" + * toUnderScoreCase("helloWorld") = "hello_world" + */ + static String toUnderScoreCase(String s) { + if (s == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + + boolean nextUpperCase = true; + + if (i < (s.length() - 1)) { + nextUpperCase = Character.isUpperCase(s.charAt(i + 1)); + } + + if ((i > 0) && Character.isUpperCase(c)) { + if (!upperCase || !nextUpperCase) { + sb.append(SEPARATOR); + } + upperCase = true; + } else { + upperCase = false; + } + + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 获取ip地址 + */ + public static String getIp(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + String comma = ","; + String localhost = "127.0.0.1"; + if (ip.contains(comma)) { + ip = ip.split(",")[0]; + } + if (localhost.equals(ip)) { + // 获取本机真正的ip地址 + try { + ip = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + log.error(e.getMessage(), e); + } + } + return ip; + } + + /** + * 根据ip获取详细地址 + */ + @SneakyThrows + public static String getCityInfo(String ip) { + if (ipLocal) { + return getLocalCityInfo(ip); + } else { + return getHttpCityInfo(ip); + } + } + + /** + * 根据ip获取详细地址 + */ + public static String getHttpCityInfo(String ip) { + String host = "202.108.22.5"; + //超时应该在3钞以上 + int timeOut = 3000; + boolean status = false; + try { + status = InetAddress.getByName(host).isReachable(timeOut); + } catch (IOException e) { + log.error(e.getMessage());; + } + String api =""; + if (status){ + api = HttpUtil.get(String.format(Constant.Url.IP_URL, ip)); + }else { + api = "{\"ip\":\"127.0.0.1\",\"pro\":\"\",\"proCode\":\"999999\",\"city\":\"\",\"cityCode\":\"0\",\"region\":\"\",\"regionCode\":\"0\",\"addr\":\" 局域网\",\"regionNames\":\"\",\"err\":\"noprovince\"}"; + } + JSONObject object = JSONUtil.parseObj(api); + return object.get("addr", String.class); + } + + + /** + * 根据ip获取详细地址 + */ + public static String getLocalCityInfo(String ip) { + try { + DataBlock dataBlock = new DbSearcher(config, file.getPath()) + .binarySearch(ip); + String region = dataBlock.getRegion(); + String address = region.replace("0|", ""); + char symbol = '|'; + if (address.charAt(address.length() - 1) == symbol) { + address = address.substring(0, address.length() - 1); + } + return address.equals(Constant.REGION) ? "内网IP" : address; + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return ""; + } + + public static String getBrowser(HttpServletRequest request) { + UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + Browser browser = userAgent.getBrowser(); + return browser.getName(); + } + + /** + * 获得当天是周几 + */ + public static String getWeekDay() { + String[] weekDays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + + int w = cal.get(Calendar.DAY_OF_WEEK) - 1; + if (w < 0) { + w = 0; + } + return weekDays[w]; + } + + /** + * 获取当前机器的IP + * + * @return / + */ + public static String getLocalIp() { + try { + InetAddress candidateAddress = null; + // 遍历所有的网络接口 + for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements();) { + NetworkInterface anInterface = interfaces.nextElement(); + // 在所有的接口下再遍历IP + for (Enumeration inetAddresses = anInterface.getInetAddresses(); inetAddresses.hasMoreElements();) { + InetAddress inetAddr = inetAddresses.nextElement(); + // 排除loopback类型地址 + if (!inetAddr.isLoopbackAddress()) { + if (inetAddr.isSiteLocalAddress()) { + // 如果是site-local地址,就是它了 + return inetAddr.getHostAddress(); + } else if (candidateAddress == null) { + // site-local类型的地址未被发现,先记录候选地址 + candidateAddress = inetAddr; + } + } + } + } + if (candidateAddress != null) { + return candidateAddress.getHostAddress(); + } + // 如果没有发现 non-loopback地址.只能用最次选的方案 + InetAddress jdkSuppliedAddress = InetAddress.getLocalHost(); + if (jdkSuppliedAddress == null) { + return ""; + } + return jdkSuppliedAddress.getHostAddress(); + } catch (Exception e) { + return ""; + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/SystemUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/SystemUtils.java new file mode 100644 index 0000000..4aa529d --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/SystemUtils.java @@ -0,0 +1,291 @@ +package com.yfd.platform.utils; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.springframework.beans.BeanUtils; +import org.springframework.cglib.beans.BeanMap; +import org.springframework.transaction.interceptor.TransactionAspectSupport; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; + +/** + * @author liwt date 2022/6/2212:11 + * @title: SystemUtils + */ +public class SystemUtils { + + public static HttpServletRequest getRequest() + { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + return request; + } + + public static HttpServletResponse getResponse() + { + HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); + return response; + } + + public static Page setPage(Map json) + { + Page page = new Page(); + page.setSize(json.get("size") == null ? 0 : Integer.parseInt(json.get("size").toString())); + page.setCurrent(json.get("current") == null ? 1 : Integer.parseInt(json.get("current").toString())); + return page; + } + + public static Page setPage(Page page) + { + HttpServletRequest request = getRequest(); + + String pageSize = request.getParameter("pageSize"); + if(StringUtils.isNotEmpty(pageSize)){ + page.setSize(Long.parseLong(pageSize)); + } + String pageNo = request.getParameter("pageNo"); + if(StringUtils.isNotEmpty(pageNo)){ + page.setCurrent(Long.parseLong(pageNo)); + } + + return page; + } + public static T mapToBean(Map map, Class clazz) throws Exception + { + T bean = clazz.newInstance(); + BeanMap beanMap = BeanMap.create(bean); + beanMap.putAll(map); + return bean; + } + + + + public static void setRollbackOnly() + { + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + } + + + /** + * + * @param a 有值得类,向b复制 + * @param b 一般来说是空的类 + */ + public static void copyProperties(Object a,Object b){ + BeanUtils.copyProperties(a,b ); + } + + + + private static SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd"); + private static SimpleDateFormat simpleDateTimeFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + + public static String getDateByDateTime(String dateTime) { + if(StringUtils.isEmpty(dateTime)){ + return ""; + } + String format; + try { + format = simpleDateFormat.format(simpleDateFormat.parse(dateTime)); + }catch (Exception e){ + return ""; + } + return format; + } + + public static String getDateByDateTime(Date dateTime){ + if(dateTime==null){ + return ""; + } + String format = simpleDateFormat.format(dateTime); + return format; + } + + + /** + * 补0 + * @param data 数据 + * @param dataLength 最终长度 + * @return + */ + public static String getZeroFill(Object data,Integer dataLength){ + return String.format("%0" + dataLength + "d", Integer.parseInt(data.toString())); + } + + /** + * 将时间格式转换为string格式 yyyy-MM-dd HH:mm:ss + * @param o + * @return + */ + public static String getStringDateTimeByObject(Date o){ + return simpleDateTimeFormat.format(o); + } + + /** + * 将时间格式转换为string格式 yyyy-MM-dd + * @param o + * @return + */ + public static String getStringDateByObject(Date o){ + + return simpleDateFormat.format(o); + } + + + + public static String getNowDate(Calendar calendar){ + if(calendar==null){ + calendar=Calendar.getInstance(); + } + // 获取当前年 + int year = calendar.get(Calendar.YEAR); +// 获取当前月 + int month = calendar.get(Calendar.MONTH) + 1; +// 获取当前日 + int day = calendar.get(Calendar.DATE); +// 获取当前小时 + int hour = calendar.get(Calendar.HOUR_OF_DAY); +// 获取当前分钟 + int minute = calendar.get(Calendar.MINUTE); +// 获取当前秒 + int second = calendar.get(Calendar.SECOND); +// 获取当前是本周第几天 + int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); +// 获取当前是本月第几天 + int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); +// 获取当前是本年第几天 + int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR); + + + String thisVersion=year+getZeroFill(month,2)+getZeroFill(day,2)+getZeroFill(hour,2)+getZeroFill(minute,2); + return thisVersion; + } + + + + public static String getUUID(){ + String string = UUID.randomUUID().toString(); + string=string.replace("-",""); + return string; + } + + public static void setPageOrder(Page page,T obj){ + Object sortName = null; + Object sortOrder = null; + + if(obj!=null){ + String jsonString = JSONObject.toJSONString(obj); + HashMap hashMap = JSONObject.parseObject(jsonString, HashMap.class); + sortName = hashMap.get("sortName"); + sortOrder = hashMap.get("sortOrder"); + }else{ + HttpServletRequest request = SystemUtils.getRequest(); + sortName = request.getParameter("sortName"); + sortOrder = request.getParameter("sortOrder"); + } + + + if(sortName!=null&&StringUtils.isNotEmpty(sortName.toString())){ + List list=new ArrayList<>(); + + OrderItem orderItem = new OrderItem(); + orderItem.setColumn(sortName.toString()); + orderItem.setAsc(true); + if(sortOrder!=null&&StringUtils.isNotEmpty(sortOrder.toString())){ + String sortOrderStr = sortOrder.toString(); + if("asc".equals(sortOrderStr)){ + orderItem.setAsc(true); + }else{ + orderItem.setAsc(false); + } + } + list.add(orderItem); + page.setOrders(list); + } + + + + + + } + + + public static Map objectToMap(Object o){ + JSONObject jsonObject=(JSONObject) JSON.toJSON(o); + Map javaObject = jsonObject.toJavaObject(Map.class); + return javaObject; + } + + + public static T mapToObject(Map map, Class classObj ){ + String jsonString = JSON.toJSONString(map); + T t = JSON.parseObject(jsonString, classObj); + return t; + } + public static T objectToEntity(Object o,Class classObj ){ + String jsonString = JSON.toJSONString(o); + T t = JSON.parseObject(jsonString, classObj); + return t; + } + + + public static String getError(Exception e){ + String msg=e.getMessage(); + if(StringUtils.isNotEmpty(msg)){ + return msg; + } + + msg=e.getCause().getMessage(); + if(StringUtils.isNotEmpty(msg)){ + return msg; + } + msg=JSON.toJSONString(e); + if(StringUtils.isNotEmpty(msg)){ + return msg; + } + return "err还是null"; + } + + public static LocalDate getLocalDateByDate(Date date){ + Instant instant = date.toInstant(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + LocalDate localDate = localDateTime.toLocalDate(); + return localDate; + } + + public static LocalDateTime getLocalTimeDateByDate(Date date){ + Instant instant = date.toInstant(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + return localDateTime; + } + + public static BigDecimal getBigDecimalByString(String str){ + Double l = Double.parseDouble(str); + + BigDecimal bigDecimal = BigDecimal.valueOf(l); + + return bigDecimal; + + } + + + public static T objectToObject(Object o,Class tClass){ + String jsonString = JSON.toJSONString(o); + T javaObject = JSON.parseObject(jsonString,tClass); + return javaObject; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/TLSFTPUtils.java b/riis-system/src/main/java/com/yfd/platform/utils/TLSFTPUtils.java new file mode 100644 index 0000000..3ec1e17 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/TLSFTPUtils.java @@ -0,0 +1,662 @@ +package com.yfd.platform.utils; + +import it.sauronsoftware.ftp4j.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * + * 穿透TLS上传FTP + */ +@Slf4j +@Service +public class TLSFTPUtils { + + private static String remotePath = "/DebugLog"; + + FTPClient client = new FTPClient(); + + SSLContext sslContext = null; + + TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() { + + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + // TODO Auto-generated method stub + + } + + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + // TODO Auto-generated method stub + + } + + public X509Certificate[] getAcceptedIssuers() { + // TODO Auto-generated method stub + return null; + } + + } }; + + /** + * ftp是否已连接 + * @throws IOException + * + */ + public boolean isConnected() { + if (client.isConnected()) { + return true; + } + return false; + } + + public boolean connect(String hostname, String username, String password,int port) { + try { + sslContext = SSLContext.getInstance("SSL"); + } catch (NoSuchAlgorithmException e) { + log.error(e.getMessage()); + log.error("SSL"); + return false; + } + try { + sslContext.init(null, trustManager, new SecureRandom()); + } catch (KeyManagementException e) { + log.error(e.getMessage()); + log.error("init"); + return false; + } + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + client.setSSLSocketFactory(sslSocketFactory); + client.setSecurity(FTPClient.SECURITY_FTPS); + client.setPassive(true);//设置被动 + client.setType(FTPClient.TYPE_BINARY); + client.setCharset("gbk"); + try { + client.connect(hostname,port); + } catch (IOException | FTPIllegalReplyException | FTPException e) { + log.error(e.getMessage());; + log.error("connect"); + return false; + } + log.info("连接"+hostname+"成功!"+client.toString()); + try { + client.login(username, password); + } catch (IOException | FTPIllegalReplyException | FTPException e) { + log.error(e.getMessage());; + log.error("login"); + return false; + } + log.info("登录"+hostname+"成功!"+client.toString()); + + return true; + } + + /** + * 上传文件或目录 + * + * @param dir + * 目标文件 + * @param del + * 是否删除源文件,默认为false + * 文件或目录对象数组 + * @throws Exception + */ + public void upload(String dir,boolean del, File... files) throws Exception { + if (StringUtils.isEmpty(dir) || files == null) { + return; + } + + try { + boolean b = isDirExist(dir); + if(!b){ + mkdirs(dir); // 创建文件夹 + } + for (File file : files) { + if(file == null){ + log.error("文件不存在!!"); + continue; + } + if (file.isDirectory()) { // 上传目录 + uploadFolder(getURL(dir), file, del); + } else { + String current2 = client.currentDirectory(); + log.info(current2); + //查询ftp服务器里的图片列表 + FTPFile[] files2 = client.list(); + if(files2 != null){ + log.info(file.getName()); + for (FTPFile ftpFile:files2){ + if (ftpFile.getName().equals(file.getName())){ + String remoteFilePath = dir + "/" + file.getName(); + log.info("=================remoteFilePath==============="+remoteFilePath); + client.deleteFile(remoteFilePath); + break; + } + } + log.info("从0上传:"+file.getName()); + try { + client.upload(file); + log.info("从0上传上传成功!"); + }catch (Exception e){ + log.info("从0上传上传失败!"); + log.error(e.getMessage()); + } + }else { + client.upload(file); + } + + } + } + } finally { + logout(); + } + } + + + /** + * 上传文件或目录 + * + * @param dir + * 目标文件 + * @param files + * 文件或目录对象数组 + * @throws Exception + */ + public void upload(String dir, File... files) throws Exception { + log.info("开始执行upload"); + upload(dir, false, files); + log.info("upload执行成功"); + } + /** + * 上传文件或目录 + * + * @param dir + * 目标文件 + * @param del + * 是否删除源文件,默认为false + * 文件或目录路径数组 + * @throws Exception + */ + public void upload(String dir, boolean del, String... paths) + throws Exception { + if (dir==null || paths == null || paths.length ==0) { + return; + } + File[] files = new File[paths.length]; + for (int i = 0; i < paths.length; i++) { + files[i] = new File(paths[i]); + } + upload(dir, del, files); + } + /** + * 上传文件或目录 + * + * @param dir + * 远程目标文件 + * @param paths + * 文件或目录路径数组 + * @throws Exception + */ + public void upload(String dir, String... paths) throws Exception { + upload(dir, false, paths); + } + /** + * 上传目录 + * + * @param parentUrl + * 父节点URL + * @param file + * 目录 + * @throws Exception + */ + private void uploadFolder( URL parentUrl, File file, + boolean del) throws Exception { + client.changeDirectory(parentUrl.getPath()); + String dir = file.getName(); // 当前目录名称 + URL url = getURL(parentUrl, dir); + if (!exists(url.getPath())) { // 判断当前目录是否存在 + client.createDirectory(dir); // 创建目录 + } + client.changeDirectory(dir); + File[] files = file.listFiles(); // 获取当前文件夹所有文件及目录 + for (int i = 0; i < files.length; i++) { + file = files[i]; + if (file.isDirectory()) { // 如果是目录,则递归上传 + uploadFolder( url, file, del); + } else { // 如果是文件,直接上传 +// client.changeDirectory(url.getPath()); + String current2 = client.currentDirectory(); + log.info(current2); + FTPFile[] files2 = client.list(); + FTPFile remoteFtpFile=null; + boolean flag=false; + for (FTPFile ftpFile:files2){ + if (ftpFile.getName().equals(file.getName())){ + flag=true; + remoteFtpFile=ftpFile; + break; + } + } + if (flag){ + log.info("断点续传"); + long remote_file_size=remoteFtpFile.getSize(); + client.upload(file,remote_file_size); + log.debug("AbstractFtpEndpointComponent upload continue upload file success"); + + }else { + log.info("从0上传"); + client.upload(file); + } + if (del){ // 删除源文件 + file.delete(); + } + } + } + } + /** + * 删除文件或目录 + * + * 文件或目录数组 + * @throws Exception + */ + public void delete(String... dirs) throws Exception { + if (dirs == null || dirs.length ==0) { + return; + } + try { + int type = -1; + for (String dir : dirs) { + client.changeDirectory("/"); // 切换至根目录 + type = getFileType(dir); // 获取当前类型 + if (type == 0) { // 删除文件 + client.deleteFile(dir); + } else if (type == 1) { // 删除目录 + deleteFolder(client, getURL(dir)); + } + } + } finally { +// logout(); + } + } + /** + * 删除目录 + * + * @param client + * FTP客户端对象 + * @param url + * FTP URL + * @throws Exception + */ + private void deleteFolder(FTPClient client, URL url) throws Exception { + String path = url.getPath(); + client.changeDirectory(path); + FTPFile[] files = client.list(); + String name = null; + for (FTPFile file : files) { + name = file.getName(); + // 排除隐藏目录 + if (".".equals(name) || "..".equals(name)) { + continue; + } + if (file.getType() == FTPFile.TYPE_DIRECTORY) { // 递归删除子目录 + deleteFolder(client, getURL(url, file.getName())); + } else if (file.getType() == FTPFile.TYPE_FILE) { // 删除文件 + client.deleteFile(file.getName()); + } + } + client.changeDirectoryUp(); + client.deleteDirectory(url.getPath()); // 删除当前目录 + } + /** + * 下载文件或目录 + * + * @param localDir + * 本地存储目录 + * @param dirs + * 文件或者目录 + * @throws Exception + */ + public void download(String localDir, String... dirs) throws Exception { + if (dirs==null ||dirs.length ==0) { + return; + } + try { + File folder = new File(localDir); + if (!folder.exists()) { // 如果本地文件夹不存在,则创建 + folder.mkdirs(); + } + String localPath = null; + for (String dir : dirs) { + /*int i = dir.lastIndexOf("/"); + String subDir = dir.substring(0,i); + String subFileName = dir.substring(i+1,dir.length());*/ + client.changeDirectory("/"); // 切换至根目录 + //client.changeDirectory(subDir); + File newFile = new File(dir); //FTP文件 + localPath = localDir + "/" + newFile.getName(); + File file=null; //本地文件 = new File(localPath) + File[] files = folder.listFiles(); + boolean flag=false; + for (File fileTemp:files) { + if (fileTemp.getName().equals(newFile.getName())) { + flag = true; + file = fileTemp; + break; + } + } + if (flag){ + log.info("断点续传"); + long local_file_size=file.length(); + log.info("从"+local_file_size+"开始"); + //client.setType(FTPClient.TYPE_BINARY); + client.download(dir,file,local_file_size); + }else { + log.info("从0下载"); + file = new File(localPath); + //client.setType(FTPClient.TYPE_BINARY); + client.download(dir, file); + } + } + } finally { +// logout(); + } + } + /** + * 下载文件夹 + * + * @param client + * FTP客户端对象 + * @param url + * FTP URL + * @param localDir + * 本地存储目录 + * @throws Exception + */ + private void downloadFolder(FTPClient client, URL url, String localDir) + throws Exception { + String path = url.getPath(); + client.changeDirectory(path); + // 在本地创建当前下载的文件夹 + File folder = new File(localDir + "/" + new File(path).getName()); + if (!folder.exists()) { + folder.mkdirs(); + } + localDir = folder.getAbsolutePath(); + FTPFile[] files = client.list(); + String name = null; + for (FTPFile file : files) { + name = file.getName(); + // 排除隐藏目录 + if (".".equals(name) || "..".equals(name)) { + continue; + } + if (file.getType() == FTPFile.TYPE_DIRECTORY) { // 递归下载子目录 + downloadFolder(client, getURL(url, file.getName()), localDir); + } else if (file.getType() == FTPFile.TYPE_FILE) { // 下载文件 + File localFolder = new File(localDir); + File file1 = null; + File[] localfiles = localFolder.listFiles(); + boolean flag=false; + for (File fileTemp:localfiles) { + if (fileTemp.getName().equals(file.getName())) { + flag = true; + file1 = fileTemp; + break; + } + } + if (flag){ + log.info("断点续传"); + long local_file_size=file1.length(); + log.info("从"+local_file_size+"开始"); + client.download(name,file1,local_file_size); + }else { + log.info("从0下载"); + file1 = new File(localDir + "/" + name); + client.download(name, file1); + } + + } + } + client.changeDirectoryUp(); + } + + // /** +// * 获取目录下所有文件 +// * +// * @param dir +// * 远程目标文件 +// * @return +// * @throws Exception +// */ +// public String[] list(String dir) throws Exception { + //FTPClient client = null; +// try { + //client = getClient(); +// client.changeDirectory(dir); +// String[] values = client.listNames(); +// if (values != null) { +// // 将文件排序(忽略大小写) +// Arrays.sort(values, String::compareToIgnoreCase); +// } +// return values; +// } catch(FTPException fe) { +// // 忽略文件夹不存在的情况 +// String mark = "code=550"; +// if (!fe.toString().contains(mark)) { +// throw fe; +// } +// } finally { +// logout(); +// } +// return new String[0]; +// } + + + /** + * 创建目录 + * FTP客户端对象 + * @param dir + * 目录 + * @throws Exception + */ + private void mkdirs(String dir) throws Exception { + if (dir == null || dir.equals("")) { + return; + } + dir = FTPPathToolkit.formatPathFTP(dir); + dir = dir.replace("//", "/"); + String[] dirs = dir.split("/"); + String path = "/"; + for (int i = 0; i < dirs.length; i++) { + if(i == 0){ + path += dirs[i]; + }else { + path =path + "/" + dirs[i]; + } + + if (!isDirExist(path)) { + log.info("创建"+path+"目录"); + client.createDirectory(path);// 创建目录 + log.info("创建"+path+"目录成功"); + } + } + client.changeDirectory(path);// 进入创建的目录 + } + + + /** + * 获取FTP目录 + * + * @param url + * 原FTP目录 + * @param dir + * 目录 + * @return + * @throws Exception + */ + private URL getURL(URL url, String dir) throws Exception { + String path = url.getPath(); + if (!path.endsWith("/") && !path.endsWith("//")) { + path += "/"; + } + dir = dir.replace("//", "/"); + if (dir.startsWith("/")) { + dir = dir.substring(1); + } + path += dir; + return new URL(url, path); + } + /** + * 获取FTP目录 + * + * @param dir + * 目录 + * @return + * @throws Exception + */ + private URL getURL(String dir) throws Exception { + if (dir.startsWith("/")) { + dir = dir.substring(1); + } + return getURL(new URL("http://8.8.8.8"), dir); + } + + /** + * 判断文件或目录是否存在 + * + * FTP客户端对象 + * @param dir + * 文件或目录 + * @return + * @throws Exception + */ + private boolean exists(String dir) throws Exception { + return getFileType(dir) != -1; + + } + + //检查目录是否存在 + private boolean isDirExist(String dir) { + try { +// client.changeDirectoryUp(); +// client.changeDirectoryUp(); + log.info("当前目录:" +client.currentDirectory()+ "==========进入目录" + dir); + client.changeDirectory(dir); + log.info("进入目录 "+ dir +"成功"); + } catch (Exception e) { + log.info(dir+"目录不存在"); + return false; + } + return true; + } + + + /** + * 判断当前为文件还是目录 + * + * FTP客户端对象 + * @param dir + * 文件或目录 + * @return -1、文件或目录不存在 0、文件 1、目录 + */ + private int getFileType( String dir) { + FTPFile[] files = null; + try { + files = client.list(dir); + + } catch (Exception e) { + return -1; + } + if (files.length > 1) { + return FTPFile.TYPE_DIRECTORY; + } else if (files.length == 1) { + FTPFile f = files[0]; + if (f.getType() == FTPFile.TYPE_DIRECTORY) { + return FTPFile.TYPE_DIRECTORY; + } + String path = dir + "/" + f.getName(); + try { + int len = client.list(path).length; + if (len == 1) { + return FTPFile.TYPE_DIRECTORY; + } else { + return FTPFile.TYPE_FILE; + } + } catch (Exception e) { + return FTPFile.TYPE_FILE; + } + } else { + try { + client.changeDirectory(dir); + client.changeDirectoryUp(); + return FTPFile.TYPE_DIRECTORY; + } catch (Exception e) { + return -1; + } + } + } + + + /** + * 注销客户端连接 + * + * @param + * + * @throws Exception + */ + public void logout() { + if (client != null) { + try { + // 有些FTP服务器未实现此功能,若未实现则会出错 + client.logout(); // 退出登录 + } catch (Exception fe) { + + } finally { + if (client.isConnected()) { // 断开连接 + try { + client.disconnect(true); + } catch (IOException | FTPIllegalReplyException | FTPException e) { + log.error(e.getMessage());; + log.error("退出异常"); + } + } + } + } + } + + + /** + * 断开与远程服务器的连接 + * @throws IOException + * + */ + public void disconnect() { + if (client.isConnected()) { + try { + client.disconnect(true); + } catch (IOException | FTPIllegalReplyException | FTPException e) { + log.error(e.getMessage());; + log.error("退出异常"); + } + } + } + + +} + + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/TestFileDir.java b/riis-system/src/main/java/com/yfd/platform/utils/TestFileDir.java new file mode 100644 index 0000000..f9646c5 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/TestFileDir.java @@ -0,0 +1,81 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.config.ResponseResult; +import org.bytedeco.javacpp.presets.opencv_core; +import org.checkerframework.checker.units.qual.A; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class TestFileDir { + public static void main(String[] args) throws Exception { + + String sipMessage = "INVITE sip:170330000303060021@22.58.166.219:5060 SIP/2.0\n" + + "Via: SIP/2.0/UDP 22.58.160.19:5060;branch=z9hG4bKfa0e9e68ba6a17b28ec894f73309fe46e.0\n" + + "Via: SIP/2.0/UDP 22.58.160.19:5083;rport=5083;branch=z9hG4bK1223840924\n" + + "Record-Route: \n" + + "From: ;tag=1759705377\n" + + "To: \n" + + "Call-ID: 768969706\n" + + "CSeq: 20 INVITE\n" + + "Contact: \n" + + "Max-forwards: 69\n" + + "User-agent: eXosip/5.0.0\n" + + "Content-Type: application/sdp\n" + + "Content-Length: 199\n" + + "\n" + + "v=0\n" + + "o=- 0 0 IN IP4 22.58.160.19\n" + + "s=Play\n" + + "u=\n" + + "c=IN IP4 22.58.160.19\n" + + "m=video 9000 RTP/AVP 108\n" + + "y=382557257\n" + + "a=mime:\n" + + "a=rtpmap:108 H265/90000\n" + + "a=fmtp:108 CIF=1;4CIF=1;F=1;K=1\n" + + "a=rate:sub\n" + + "a=recvonly"; // 在这里替换为您的SIP消息 + + // 找到SDP内容的开始位置(即Content-Type头部之后的空行) + + + // 查找以m=开头的行 + String[] sdpLines = sipMessage.split("\r\n"); + for (String line : sdpLines) { + if (line.startsWith("m=")) { + // 提取动态载荷类型(PT) + String[] parts = line.split(" "); + if (parts.length >= 4 && "video".equals(parts[1])) { + // 假设第三个字段是RTP/AVP,第四个字段是PT + String pt = parts[3].split("/")[1]; // 分割RTP/AVP和PT + System.out.println("提取到的PT是: " + pt); + break; // 找到后退出循环 + } + } + } + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/TestReadChar.java b/riis-system/src/main/java/com/yfd/platform/utils/TestReadChar.java new file mode 100644 index 0000000..3cef1b2 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/TestReadChar.java @@ -0,0 +1,102 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.modules.patroltask.domain.TaskResult; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class TestReadChar { + public static void main(String[] args) throws IOException { + String localFilePath = "D:\\catalina.out"; + boolean b = false; + int count = 0; + int count1 = 0; +// File file = new File("D:\\莱达四维\\hangben.txt"); +// FileWriter writer = new FileWriter(file); + try (BufferedReader br = new BufferedReader(new FileReader(localFilePath))) { + String line = ""; + while ((line = br.readLine()) != null) { + if (!b && line.contains("智能分析识别通知")) { + b = true; + } + if (line.contains("智能分析识别通知") && line.contains("5901e99bd88744ffa88285d6274bcaec")) { +// System.out.println(line); +// line = line.replace("智能分析识别通知:", ""); + line = "{" + StrUtil.subAfter(line, "{", false); + JSONObject jsonObject = JSONUtil.parseObj(line); + JSONArray resultList = jsonObject.getJSONArray("resultList"); +// if (resultList.size() > 1) { +// count1++; +// } + for (int i = 0; i < resultList.size(); i++) { + JSONObject resultObject = resultList.getJSONObject(i); + if (resultObject == null) { + continue; + } + count++; + //7FE4BB37-F54A-4808-852B-6CAB43DD71B3-00001/2024/08/18/task_laida_21/CCD/正常SF6ylb (32)_原图_ori.jpg + JSONArray results = resultObject.getJSONArray("results"); + JSONObject result1 = results.getJSONObject(0); + if(result1==null){ + count1++; + continue; + } + String filename = result1.getStr("resImagePath"); + String fileName = StrUtil.subAfter(filename, "/", true).replace("_原图.jpg", ".txt").replace("_原图.jpeg", ".txt").replace("_原图.JPG", ".txt").replace("_原图.png", ".txt").replace("_原图_ori.jpg", ".txt").replace("_原图_ori.jpeg", ".txt").replace("_原图_ori.JPG", ".txt").replace("_原图_ori.png", ".txt").replace("_原图_infer.jpg", ".txt").replace("_原图_infer.jpeg", ".txt").replace("_原图_infer.JPG", ".txt").replace("_原图_infer.png", ".txt"); + File file = new File("D:\\海康威视\\txt\\" + fileName); +// if (FileUtil.exist(file)) { +// count1++; +// } + FileWriter writer = new FileWriter(file); + writer.write(""); + for (int a = 0; a < results.size(); a++) { + JSONObject result = results.getJSONObject(a); + String code = result.getStr("code"); + if ("2000".equals(code)) { + String posStr = ""; + String pos = result.getStr("pos"); + // 将框坐标统一存储画框 + if (StrUtil.isNotBlank(pos)) { + JSONArray jsonArray = JSONUtil.parseArray(pos); + // 写入内容到文件 + if (jsonArray.size() > 0) { + JSONObject posObject = jsonArray.getJSONObject(0); + if (jsonObject.size() > 0) { + JSONArray areas = posObject.getJSONArray("areas"); + JSONObject jsonObject1 = areas.getJSONObject(0); + JSONObject jsonObject2 = areas.getJSONObject(1); + posStr = " " + jsonObject1.getStr("x") + " " + jsonObject1.getStr("y") + " " + jsonObject2.getStr("x") + " " + jsonObject2.getStr("y"); + } + } + + } + String resultStr = ""; + if ("1".equals(result.getStr("value"))) { + resultStr = result.getStr("type") + " " + result.getStr("conf") + posStr + "\n"; + } + writer.write(resultStr); + } + } + writer.close(); + } +// writer.write(line + "\n"); +// System.out.println(line); +// JSONObject jsonObject = JSONUtil.parseObj(line); +// JSONArray resultList = jsonObject.getJSONArray("resultList"); + + } + + } + } catch (Exception e) { + e.printStackTrace(); + } +// writer.close(); + System.out.println("分析返回数量" + count + "结果返回数量" + count1); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/TestUdpUtil.java b/riis-system/src/main/java/com/yfd/platform/utils/TestUdpUtil.java new file mode 100644 index 0000000..dd6b5ca --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/TestUdpUtil.java @@ -0,0 +1,195 @@ +package com.yfd.platform.utils; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.google.zxing.*; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; +import com.yfd.platform.modules.patroltask.domain.TaskTodo; +import org.bytedeco.javacpp.avcodec; +import org.bytedeco.javacv.FFmpegFrameGrabber; +import org.bytedeco.javacv.FFmpegFrameRecorder; +import org.bytedeco.javacv.Frame; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipOutputStream; + +public class TestUdpUtil { + + public static void main(String[] args) throws Exception { + String inputFilePath = "D:\\ffmpeg\\bin\\input.mp4"; // 输入视频文件路径 + String outputRTPUrl = "rtp://192.168.22.237:5083"; // RTP输出URL + + FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFilePath); + FFmpegFrameRecorder recorder = null; + + try { + grabber.start(); + + // 设置输出参数 + recorder = new FFmpegFrameRecorder(outputRTPUrl, grabber.getImageWidth(), grabber.getImageHeight()); + recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 设置H.264编码 + recorder.setFormat("rtp"); // 设置输出格式为RTP(注意:这里的格式设置可能需要根据实际情况调整) + recorder.setFrameRate(grabber.getFrameRate()); + recorder.setAudioChannels(2); + recorder.start(); + + Frame frame; + while ((frame = grabber.grab()) != null) { + // 在这里可以对frame进行处理(如果需要的话) + recorder.record(frame); + } + + grabber.stop(); + recorder.stop(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (grabber != null) { + grabber.release(); + } + if (recorder != null) { + recorder.release(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + + public static byte[] unlong2H4bytes(long n) { + byte[] b = new byte[2]; + b[0] = (byte) (n & 0xff); + b[1] = (byte) (n >> 8 & 0xff); + return b; + } + + public static String byte2Hex(byte[] bytes) { + StringBuilder hexString = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + String hex = Integer.toHexString(0xff & bytes[i]); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + private static void updateTaskTodo(TaskTodo taskTodo) { + taskTodo.setTaskState("1"); + } + + /** + * 压缩单文件 + * + * @param file + * @param zos + * @param buffer + */ + public static void compressSingleFile(File file, ZipOutputStream zos, byte[] buffer) { + int len; + try (FileInputStream fis = new FileInputStream(file)) { + while ((len = fis.read(buffer)) > 0) { + zos.write(buffer, 0, len); + zos.flush(); + } + zos.closeEntry(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /********************************** + * 用途说明: 发送一个文件 + * 参数说明 + * 返回值说明: void + ***********************************/ + public static void sendFile(String localHost, int sendPort, int receivePort) { + try { + System.out.println("--------------------开始发送---------------------"); + //创建socket,设置发送端端口号 + DatagramSocket clientSocket = new DatagramSocket(sendPort); + //要传输的文件路径 + File source = new File("D:\\测试.txt"); + InputStream inputstream = new FileInputStream(source); + byte[] bytes = new byte[1024]; + if (inputstream.read(bytes) != -1) { + DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(localHost), receivePort); + //datagramPacket.setAddress(InetAddress.getLocalHost()); + System.out.println(InetAddress.getLocalHost()); + InetSocketAddress unresolved = InetSocketAddress.createUnresolved(localHost, receivePort); + System.out.println(unresolved); + //设置目的端口号 + //datagramPacket.setPort(receivePort); + //存入数据到packet + datagramPacket.setData(bytes); + //发送数据 + clientSocket.send(datagramPacket); + } + clientSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + /********************************** + * 用途说明: 解析二维码 + * 参数说明 file + * 返回值说明: java.lang.String + ***********************************/ + public static String resolveCodeByFile(String path) { + String content = null; + BufferedImage image; + try { + image = ImageIO.read(new File(path)); + LuminanceSource source = new BufferedImageLuminanceSource(image); + Binarizer binarizer = new HybridBinarizer(source); + BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); + Map hints = new HashMap(); + hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); + Result result = new MultiFormatReader().decode(binaryBitmap, hints);//解码 + System.out.println("图片中内容: "); + System.out.println("content: " + result.getText()); + content = result.getText(); + } catch (IOException e) { + e.printStackTrace(); + } catch (NotFoundException e) { + //这里判断如果识别不了带LOGO的图片,重新添加上一个属性 + try { + image = ImageIO.read(new File(path)); + LuminanceSource source = new BufferedImageLuminanceSource(image); + Binarizer binarizer = new HybridBinarizer(source); + BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); + Map hints = new HashMap<>(); + //设置编码格式 + hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); + //设置优化精度 + hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); + //设置复杂模式开启(我使用这种方式就可以识别微信的二维码了) + hints.put(DecodeHintType.PURE_BARCODE, Boolean.TYPE); + Result result = new MultiFormatReader().decode(binaryBitmap, hints);//解码 + System.out.println("图片中内容: "); + System.out.println("content: " + result.getText()); + content = result.getText(); + } catch (Exception e1) { + e1.printStackTrace(); + } + } + return content; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/VideoToImageUtil.java b/riis-system/src/main/java/com/yfd/platform/utils/VideoToImageUtil.java new file mode 100644 index 0000000..47fbb71 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/VideoToImageUtil.java @@ -0,0 +1,121 @@ +package com.yfd.platform.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; + +@Slf4j +public class VideoToImageUtil { + + + + public static void getVideoPicture(String ffmpegpath,String videoUrl, String imgfile) { + FileUtil.touch(imgfile); + // 确保文件存在 + try { + + // 构建命令数组 + String[] command = { + ffmpegpath + "ffmpeg", + "-loglevel", "quiet", + "-i", videoUrl, + "-vframes", "1", + "-y", + imgfile + }; + + log.info(Arrays.toString(command)); + // 启动FFmpeg进程 + Process process = new ProcessBuilder(command) + .redirectInput(ProcessBuilder.Redirect.INHERIT) + .redirectOutput(ProcessBuilder.Redirect.INHERIT) + .redirectError(ProcessBuilder.Redirect.INHERIT) + .start(); + + // 等待FFmpeg进程完成 + int exitCode = process.waitFor(); + if (exitCode == 0) { + log.info("视频帧提取完毕!"); + } else { + log.info("FFmpeg进程出错"); + } + + } catch (Exception e) { + log.error("发生错误", e); + throw new RuntimeException("抓取视频失败"); + } + } + + public static void saveCaptureAudioFile(String ffmpegpath, String videoUrl, String audiofile) { + try { + // 构建FFmpeg命令 + FileUtil.touch(audiofile); + String[] command = { + ffmpegpath + "ffmpeg", + "-i", videoUrl, + "-acodec", "pcm_s16le", // 更改音频编码为pcm_s16le + "-ar", "44100", + "-t", "10", + "-y", // 强制覆盖已存在的文件 + audiofile + }; + // 启动FFmpeg进程 + Process process = new ProcessBuilder(command).start(); + // 创建并启动线程处理标准输出流 + Thread outputThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + log.info(line); + } + } catch (IOException e) { + log.error(e.getMessage());; + } + }); + // 启动线程 + outputThread.start(); + + Thread errorThread = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + while ((line = reader.readLine()) != null) { + log.error(line); + } + } catch (IOException e) { + log.error(e.getMessage());; + } + }); + errorThread.start(); + + // 等待FFmpeg进程完成 + int exitCode = process.waitFor(); + if (exitCode == 0) { + log.info("声音录制完成!"); + } else { + log.error("FFmpeg进程出错,退出码:" + exitCode); + } + + } catch (Exception e) { + log.error(e.getMessage());; + throw new RuntimeException("抓取音频失败"); + } + } + + + public static void main(String[] args) { + try { + String ffmpegpath = "d:\\ZLMediaKit_Win\\ffmpeg\\bin\\"; + //String videoUrl = "rtsp://admin:JY123456@192.168.1.66:554/Streaming/Channels/101"; + String videoUrl = "rtsp://192.168.1.13:31554/rtp/34020000001110000009_34020000001310000001"; + String audioPath = "D:/test.wav"; + VideoToImageUtil.saveCaptureAudioFile(ffmpegpath,videoUrl,audioPath); + } catch (Exception e) { + log.error(e.getMessage());; + } + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm2/SM2Utils.java b/riis-system/src/main/java/com/yfd/platform/utils/sm2/SM2Utils.java new file mode 100644 index 0000000..41c3dff --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm2/SM2Utils.java @@ -0,0 +1,165 @@ +package com.yfd.platform.utils.sm2; + +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.gm.GMNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.util.encoders.Hex; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.ECGenParameterSpec; + +/** + * @ClassName SM2Utils + * @Description SM2算法工具类 + */ +@Slf4j +public class SM2Utils { + + public static KeyPair createECKeyPair() { + final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1"); + + // 获取一个椭圆曲线类型的密钥对生成器 + final KeyPairGenerator kpg; + try { + kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider()); + kpg.initialize(sm2Spec, new SecureRandom()); + + return kpg.generateKeyPair(); + } catch (Exception e) { + log.error(e.getMessage());; + return null; + } + } + + public static String encrypt(String publicKeyHex, String data) { + return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data, 1); + } + + public static String encrypt(BCECPublicKey publicKey, String data, int modeType) { + //加密模式 + SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2; + if (modeType != 1) { + mode = SM2Engine.Mode.C1C2C3; + } + ECParameterSpec ecParameterSpec = publicKey.getParameters(); + ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(), + ecParameterSpec.getG(), ecParameterSpec.getN()); + ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters); + + SM2Engine sm2Engine = new SM2Engine(mode); + + sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom())); + byte[] arrayOfBytes = null; + try { + byte[] in = data.getBytes(StandardCharsets.UTF_8); + + arrayOfBytes = sm2Engine.processBlock(in, 0, in.length); + } catch (Exception e) { + log.info("SM2加密时出现异常:" + e.getMessage()); + log.error(e.getMessage());; + } + if (arrayOfBytes == null) { + return null; + } + return Hex.toHexString(arrayOfBytes); + } + + public static String decrypt(String privateKeyHex, String cipherData) { + return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData, 1); + } + + public static String decrypt(BCECPrivateKey privateKey, String cipherData, int modeType) { + //解密模式 + SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2; + if (modeType != 1) { + mode = SM2Engine.Mode.C1C2C3; + } + + byte[] cipherDataByte = Hex.decode(cipherData); + ECParameterSpec ecParameterSpec = privateKey.getParameters(); + ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(), + ecParameterSpec.getG(), ecParameterSpec.getN()); + ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(), + ecDomainParameters); + + SM2Engine sm2Engine = new SM2Engine(mode); + sm2Engine.init(false, ecPrivateKeyParameters); + String result = null; + try { + byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length); + result = new String(arrayOfBytes, StandardCharsets.UTF_8); + } catch (Exception e) { + log.info("SM2解密时出现异常" + e.getMessage()); + } + return result; + } + + private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1"); + + private static ECParameterSpec ecDomainParameters = new ECParameterSpec(x9ECParameters.getCurve(), + x9ECParameters.getG(), x9ECParameters.getN()); + + public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) { + + if (pubKeyHex.length() > 128) { + pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128); + } + String stringX = pubKeyHex.substring(0, 64); + String stringY = pubKeyHex.substring(stringX.length()); + BigInteger x = new BigInteger(stringX, 16); + BigInteger y = new BigInteger(stringY, 16); + + ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), + ecDomainParameters); + + return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION); + } + + public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) { + BigInteger d = new BigInteger(privateKeyHex, 16); + ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters); + return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION); + } + + public static void main(String[] args) { + String publicKeyHex = null; + String privateKeyHex = null; + KeyPair keyPair = createECKeyPair(); + PublicKey publicKey = keyPair.getPublic(); + if (publicKey instanceof BCECPublicKey) { + //获取65字节非压缩缩的十六进制公钥串(0x04) + publicKeyHex = Hex.toHexString(((BCECPublicKey) publicKey).getQ().getEncoded(false)); + log.info("SM2公钥:" + publicKeyHex); + } + PrivateKey privateKey = keyPair.getPrivate(); + if (privateKey instanceof BCECPrivateKey) { + //获取32字节十六进制私钥串 + privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16); + log.info("SM2私钥:" + privateKeyHex); + } + + String data = "Admin123qaz@@1"; + + //将十六进制公钥串转换为 BCECPublicKey 公钥对象 + String encryptData = encrypt(publicKeyHex, data); + log.info(encryptData.length()+""); + log.info("加密结果:" + encryptData); + + //将十六进制私钥串转换为 BCECPrivateKey 私钥对象 + data = decrypt(privateKeyHex, encryptData); + log.info("解密结果:" + data); + } +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm3/SM3Utils.java b/riis-system/src/main/java/com/yfd/platform/utils/sm3/SM3Utils.java new file mode 100644 index 0000000..cda11af --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm3/SM3Utils.java @@ -0,0 +1,45 @@ +package com.yfd.platform.utils.sm3; + +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; + +@Slf4j +public class SM3Utils { + + /** + * sm3算法加密,不可逆加密 + * @param plainText 需加密的明文字符串 + * @return 加密后固定长度64的16进制字符串 + */ + public static String encrypt(String plainText) { + return SecretCommon.encrypt(plainText); + } + + /** + * sm3算法加密,检测密码是否一致 + * @param plainText 需加密的明文字符串 + * @param cryptText 已加密字符串 + * @return 判断是否匹配 + */ + public static boolean matches(String plainText,String cryptText) { + String newcrptString=encrypt(plainText); + boolean matches=false; + if(newcrptString.equals(cryptText)){ + matches=true; + } + return matches; + } + + /** + * sm3算法通过密钥进行加密,不可逆加密 + * @param keyText 密钥字符串 + * @param plainText 需加密的明文字符串 + * @return 加密后固定长度64的16进制字符串 + */ + public static String encryptByKey(String keyText, String plainText) { + return SecretCommon.encryptByKey(keyText, plainText); + } + + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm3/SecretCommon.java b/riis-system/src/main/java/com/yfd/platform/utils/sm3/SecretCommon.java new file mode 100644 index 0000000..b568456 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm3/SecretCommon.java @@ -0,0 +1,66 @@ +package com.yfd.platform.utils.sm3; + +import org.bouncycastle.crypto.digests.SM3Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; + +/** + * SM3密码杂凑算法(哈希算法) + * SM3杂凑算法是我国自主设计的密码杂凑算法。 + * 适用于商用密码应用中的数字签名和验证消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。 + * 为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短。 + * 例如MD5输出128比特杂凑值,输出长度太短,影响其安全性SHA-1算法的输出长度为160比特,SM3算法的输出长度为256比特,因此SM3算法的安全性要高于MD5算法和SHA-1算法。 + */ +public class SecretCommon { + + /** + * sm3算法加密,不可逆加密 + * @param plainText 需加密的明文字符串 + * @return 加密后固定长度64的16进制字符串 + */ + public static String encrypt(String plainText) { + return ByteUtils.toHexString(encrypt(plainText.getBytes())); + } + + /** + * sm3算法加密,不可逆加密 + * @param plainByte 需加密的明文数组 + * @return 加密后固定长度64的16进制数组 + */ + public static byte[] encrypt(byte[] plainByte) { + SM3Digest sm3Digest = new SM3Digest(); + sm3Digest.update(plainByte, 0, plainByte.length); + byte[] digestByte = new byte[sm3Digest.getDigestSize()]; + sm3Digest.doFinal(digestByte, 0); + return digestByte; + } + + /** + * sm3算法通过密钥进行加密,不可逆加密 + * @param keyText 密钥字符串 + * @param plainText 需加密的明文字符串 + * @return 加密后固定长度64的16进制字符串 + */ + public static String encryptByKey(String keyText, String plainText) { + return ByteUtils.toHexString(encryptByKey(keyText.getBytes(), plainText.getBytes())); + } + + /** + * sm3算法通过密钥进行加密,不可逆加密 + * @param keyByte 密钥数组 + * @param plainByte 需加密的明文数组 + * @return 加密后固定长度64的16进制数组 + */ + public static byte[] encryptByKey(byte[] keyByte, byte[] plainByte) { + KeyParameter keyParameter = new KeyParameter(keyByte); + SM3Digest sm3Digest = new SM3Digest(); + HMac hMac = new HMac(sm3Digest); + hMac.init(keyParameter); + hMac.update(plainByte, 0, plainByte.length); + byte[] result = new byte[hMac.getMacSize()]; + hMac.doFinal(result, 0); + return result; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm4/ConfigBean.java b/riis-system/src/main/java/com/yfd/platform/utils/sm4/ConfigBean.java new file mode 100644 index 0000000..59f5b10 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm4/ConfigBean.java @@ -0,0 +1,29 @@ +package com.yfd.platform.utils.sm4; + +public class ConfigBean { + + /** + * 秘钥空间大小 + */ + public static final int SM4_KEY_SIZE = 256; + + /** + * 算法编号 + */ + public static final String ALGORITHM_NAME = "SM4"; + + /** + * 首次加密初始向量 01030507090B0D0F11131517191B1D1F + */ + public static final byte[] SM4_KEY_IV = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 }; + + /** + * ECB模式串 + */ + public static final String ALGORITHM_ECB_PADDING = "SM4/ECB/PKCS5Padding"; + /** + * CBC模式串 + */ + public static final String ALGORITHM_CBC_PADDING = "SM4/CBC/PKCS5Padding"; + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm4/ProviderSingleton.java b/riis-system/src/main/java/com/yfd/platform/utils/sm4/ProviderSingleton.java new file mode 100644 index 0000000..d73a75b --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm4/ProviderSingleton.java @@ -0,0 +1,25 @@ +package com.yfd.platform.utils.sm4; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class ProviderSingleton { + + private static BouncyCastleProvider instance = null; + + private ProviderSingleton() { + } + + private static synchronized void syncInit() { + if (instance == null) { + instance = new BouncyCastleProvider(); + } + } + + public static BouncyCastleProvider getInstance() { + if (instance == null) { + syncInit(); + } + return instance; + } + +} diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm4/SM4Utils.java b/riis-system/src/main/java/com/yfd/platform/utils/sm4/SM4Utils.java new file mode 100644 index 0000000..a22f250 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm4/SM4Utils.java @@ -0,0 +1,74 @@ +package com.yfd.platform.utils.sm4; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +public class SM4Utils { + + /** + * 生成秘钥 + */ + public static String generateKey() throws NoSuchAlgorithmException { + return SecretCommon.generateKey().substring(0, 16); + } + + /** + * 默认加密方法(ECB) + */ + public static String encrypt(String plainText, String keyText) throws Exception, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return encryptECB(plainText, keyText); + } + + /** + * 默认解密方法(ECB) + */ + public static String decrypt(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return decryptECB(cipherText, keyText); + } + + /** + * ECB模式,加密 + * @param plainText 明文字符串 + * @param keyText 秘钥内容 + */ + public static String encryptECB(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return SecretCommon.encryptECB(plainText, keyText); + } + + /** + * ECB模式,解密 + * @param cipherText 密文字符串 + * @param keyText 秘钥内容 + */ + public static String decryptECB(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return SecretCommon.decryptECB(cipherText, keyText); + } + + /** + * CBC模式,加密 + * @param plainText 明文字符串 + * @param keyText 秘钥内容 + */ + public static String encryptCBC(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return SecretCommon.encryptCBC(plainText, keyText); + } + + /** + * CBC模式,解密 + * @param cipherText 密文字符串 + * @param keyText 秘钥内容 + */ + public static String decryptCBC(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return SecretCommon.decryptCBC(cipherText, keyText); + } + + +} + + + diff --git a/riis-system/src/main/java/com/yfd/platform/utils/sm4/SecretCommon.java b/riis-system/src/main/java/com/yfd/platform/utils/sm4/SecretCommon.java new file mode 100644 index 0000000..4c8cc24 --- /dev/null +++ b/riis-system/src/main/java/com/yfd/platform/utils/sm4/SecretCommon.java @@ -0,0 +1,120 @@ +package com.yfd.platform.utils.sm4; + + + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; + +/** + * SM4分组对称密码算法(对称算法) + * SM4分组密码算法是我国自主设计的分组对称密码算法,用于实现数据的加密/解密运算,以保证数据和信息的机密性。 + * 要保证一个对称密码算法的安全性的基本条件是其具备足够的密钥长度,SM4算法与AES算法具有相同的密钥长度分组长度128比特,因此在安全性上高于3DES算法。 + */ +public class SecretCommon { + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + /** + * 生成秘钥 + */ + public static String generateKey() throws NoSuchAlgorithmException { + return ByteUtils.toHexString(generateKeyByte(ConfigBean.SM4_KEY_SIZE, ProviderSingleton.getInstance())); + } + + /** + * 生成秘钥 + */ + public static String generateKeyBC() throws NoSuchProviderException, NoSuchAlgorithmException { + return ByteUtils.toHexString(generateKeyByte(ConfigBean.SM4_KEY_SIZE, BouncyCastleProvider.PROVIDER_NAME)); + } + + public static byte[] generateKeyByte(int keySize, String var) throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator keyGenerator = KeyGenerator.getInstance(ConfigBean.ALGORITHM_NAME, var); + keyGenerator.init(keySize, new SecureRandom()); + return keyGenerator.generateKey().getEncoded(); + } + + public static byte[] generateKeyByte(int keySize, BouncyCastleProvider var) throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance(ConfigBean.ALGORITHM_NAME, var); + keyGenerator.init(keySize, new SecureRandom()); + return keyGenerator.generateKey().getEncoded(); + } + + /** + * ECB模式,加密 + * @param plainText 明文字符串 + * @param keyText 秘钥内容 + */ + public static String encryptECB(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return Base64.encodeBase64String(cipherECB(Cipher.ENCRYPT_MODE, plainText.getBytes(StandardCharsets.UTF_8), keyText.getBytes())); + } + + /** + * ECB模式,解密 + * @param cipherText 密文字符串 + * @param keyText 秘钥内容 + */ + public static String decryptECB(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return new String(cipherECB(Cipher.DECRYPT_MODE, Base64.decodeBase64(cipherText), keyText.getBytes())); + } + + /** + * ECB模式,加解密算法基础方法 + * @param mode 加解密模式 Cipher.ENCRYPT_MODE 1:加密/ Cipher.DECRYPT_MODE 2:解密 + * @param plainByte 需加密明文内容/待解密密文内容 + * @param keyByte 秘钥内容 + */ + public static byte[] cipherECB(int mode, byte[] plainByte, byte[] keyByte) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(ConfigBean.ALGORITHM_ECB_PADDING, ProviderSingleton.getInstance()); + cipher.init(mode, new SecretKeySpec(keyByte, ConfigBean.ALGORITHM_NAME)); + return cipher.doFinal(plainByte); + } + + /** + * CBC模式,加密 + * @param plainText 明文字符串 + * @param keyText 秘钥内容 + */ + public static String encryptCBC(String plainText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return Base64.encodeBase64String(cipherCBC(Cipher.ENCRYPT_MODE, plainText.getBytes(StandardCharsets.UTF_8), keyText.getBytes())); + } + + /** + * CBC模式,解密 + * @param cipherText 密文字符串 + * @param keyText 秘钥内容 + */ + public static String decryptCBC(String cipherText, String keyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + return new String(cipherCBC(Cipher.DECRYPT_MODE, Base64.decodeBase64(cipherText), keyText.getBytes())); + } + + /** + * CBC模式,加解密算法基础方法 + * @param mode 加解密模式 Cipher.ENCRYPT_MODE 1:加密/ Cipher.DECRYPT_MODE 2:解密 + * @param plainByte 需加密明文内容/待解密密文内容 + * @param keyByte 秘钥内容 + */ + public static byte[] cipherCBC(int mode, byte[] plainByte, byte[] keyByte) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(ConfigBean.ALGORITHM_CBC_PADDING, ProviderSingleton.getInstance()); + cipher.init(mode, new SecretKeySpec(keyByte, ConfigBean.ALGORITHM_NAME), new IvParameterSpec(ConfigBean.SM4_KEY_IV)); + return cipher.doFinal(plainByte); + } + +} diff --git a/riis-system/src/main/resources/all-application.yml b/riis-system/src/main/resources/all-application.yml new file mode 100644 index 0000000..96c00e0 --- /dev/null +++ b/riis-system/src/main/resources/all-application.yml @@ -0,0 +1,163 @@ +# 此配置文件只是用作展示所有配置项, 不可不直接使用 +spring: + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 6 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + password: + # [可选] 超时时间 + timeout: 10000 + # [可选] 一个pool最多可分配多少个jedis实例 + poolMaxTotal: 1000 + # [可选] 一个pool最多有多少个状态为idle(空闲)的jedis实例 + poolMaxIdle: 500 + # [可选] 最大的等待时间(秒) + poolMaxWait: 5 + # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 + datasource: + # 使用mysql 打开23-28行注释, 删除29-36行 + name: wvp + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: + password: + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + # name: eiot +# url: jdbc:sqlite::resource:wvp.sqlite +# username: +# password: +# type: com.alibaba.druid.pool.DruidDataSource +# driver-class-name: org.sqlite.JDBC + max-active: 1 + min-idle: 1 + +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 + # [可选] HTTPS配置, 默认不开启 + ssl: + # [可选] 是否开启HTTPS访问 + enabled: false + # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 + key-store: classpath:xxx.jks + # [可选] 证书密码 + key-password: password + # [可选] 证书类型, 默认为jks,根据实际修改 + key-store-type: JKS + +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP, 必须是网卡上的IP,用于sip下协议栈监听ip,如果监听所有设置为0.0.0.0 + monitor-ip: 0.0.0.0 + # [必须修改] 本机的IP + ip: 192.168.0.100 + # [可选] 28181服务监听的端口 + port: 5060 + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) + # 后两位为行业编码,定义参照附录D.3 + # 3701020049标识山东济南历下区 信息行业接入 + # [可选] + domain: 4401020049 + # [可选] + id: 44010200492000000001 + # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 + password: admin123 + # [可选] 心跳超时时间, 建议设置为心跳周期的三倍 + keepalive-timeout: 255 + # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 + register-time-interval: 60 + # [可选] 云台控制速度 + ptz-speed: 50 + # TODO [可选] 收到心跳后自动上线, 重启服务后会将所有设备置为离线,默认false,等待注册后上线。设置为true则收到心跳设置为上线。 + # keepalliveToOnline: false + +#zlm 默认服务器配置 +media: + # [可选] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: + # [必须修改] zlm服务器的内网IP + ip: 192.168.0.100 + # [可选] 返回流地址时的ip,置空使用 media.ip + stream-ip: + # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip + sdp-ip: + # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip + hook-ip: + # [必须修改] zlm服务器的http.port + http-port: 80 + # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 + http-ssl-port: + # [可选] zlm服务器的rtmp.port, 置空使用zlm配置文件配置 + rtmp-port: + # [可选] zlm服务器的rtmp.sslport, 置空使用zlm配置文件配置 + rtmp-ssl-port: + # [可选] zlm服务器的 rtp_proxy.port, 置空使用zlm配置文件配置 + rtp-proxy-port: + # [可选] zlm服务器的 rtsp.port, 置空使用zlm配置文件配置 + rtsp-port: + # [可选] zlm服务器的 rtsp.sslport, 置空使用zlm配置文件配置 + rtsp-ssl-port: + # [可选] 是否自动配置ZLM, 如果希望手动配置ZLM, 可以设为false, 不建议新接触的用户修改 + auto-config: true + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # [可选] zlm服务器的general.streamNoneReaderDelayMS + stream-none-reader-delay-ms: 18000 # 无人观看多久自动关闭流, -1表示永不自动关闭,即 关闭按需拉流 + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, + port-range: 30000,30500 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 0 + +# [可选] 日志配置, 一般不需要改 +logging: + file: + name: logs/wvp.log + max-history: 30 + max-size: 10MB + total-size-cap: 300MB + level: + com.genersoft.iot: debug + com.genersoft.iot.vmp.storager.dao: info + com.genersoft.iot.vmp.gb28181: info +# [根据业务需求配置] +user-settings: + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: false + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播等待超时时间,单位:毫秒 + play-timeout: 3000 + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 + wait-track: false + # 是否开启接口鉴权 + interface-authentication: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: true + # 是否将日志存储进数据库 + logInDatebase: true + +# 在线文档: swagger-ui(生产环境建议关闭) +swagger-ui: + enabled: true + +# 版本信息, 不需修改 +version: + version: "@project.version@" + description: "@project.description@" + artifact-id: "@project.artifactId@" \ No newline at end of file diff --git a/riis-system/src/main/resources/application-dev.yml b/riis-system/src/main/resources/application-dev.yml new file mode 100644 index 0000000..f2f8dab --- /dev/null +++ b/riis-system/src/main/resources/application-dev.yml @@ -0,0 +1,267 @@ +server: + port: 8090 + appname: + +spring: + #应用名称 + application: + name: Project-plateform + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://43.138.168.68:3306/smartsubstationdb?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: root + password: ylfw20230626@ #ylfw20230626@ #ylfw20230626@ + #initial-size: 5 + # max-active: 10 + #max-wait: 20000 # 最大等待时间,单位毫秒 + #min-idle: 2 + #time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + #min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒 + backuppath: d:\riis\databackup\ + + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + #host: 127.0.0.1 #82.156.18.154 + host: 127.0.0.1 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 0 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + # password: ZGHszy741230 + # [可选] 超时时间 + timeout: 1000000 + # mysql数据源 + + mvc: + pathmatch: + matching-strategy: ant_path_matcher + servlet: + multipart: + max-file-size: 100MB + max-request-size: 200MB +# mail: +# #smtp服务主机 qq邮箱则为smtp.qq.com +# host: smtp.qq.com +# #服务协议 +# protocol: smtp +# # 编码集 +# default-encoding: UTF-8 +# #发送邮件的账户 +# username: 1750840654@qq.com +# #授权码 +# password: nwsefzydtnwuehcd +# test-connection: true +# properties: +# mail: +# smtp: +# auth: true +# starttls: +# enable: true +# required: true +### 特殊情况下,查询结果返回List格式,会自动将查询结果为null的字段忽略,加入该配置则不会忽略 +mybatis-plus: + configuration: + call-setters-on-nulls: true + +logging: + file: + name: logs/projectname.log + level: + com.genersoft.iot: debug + com.genersoft.iot.vmp.storager.dao: info + com.genersoft.iot.vmp.gb28181: info + +# 在线文档: swagger-ui(生产环境建议关闭) +swagger-ui: + enabled: true + +# 登录相关配置 +login: + # 登录缓存 + cache-enable: true + # 是否限制单用户登录 + single-login: false + # 验证码 + login-code: + # 验证码类型配置 查看 LoginProperties 类 + code-type: arithmetic + # 登录图形验证码有效时间/分钟 + expiration: 2 + # 验证码高度 + width: 111 + # 验证码宽度 + heigth: 36 + # 内容长度 + length: 2 + # 字体名称,为空则使用默认字体 + font-name: + # 字体大小 + font-size: 25 +# IP 本地解析 +ip: + local-parsing: true + +httpserver: #配置http请求访问的地址 + monitorserver: #视频监控服务器(与边缘节点同一IP) + ip: 192.168.1.20 + port: 18080 + rtspport: 554 + appname: + + meidiaserver: #流媒体服务器(与边缘节点同一IP) + ip: 192.168.1.20 + port: 82 + secret: QUdZ1Gk4axB57kVvn0UKnzdGGHvpROvI + + analyseserver: #智能分析主机服务器 + ip: 192.168.1.173 + port11: 20011 #分析调用端口 + port12: 20011 #静默监视任务端口 + port13: 20013 #算法更新端口 + callanalyse: true + sdkserver: #第三方sdk服务 + ip: 192.168.1.20 + haikangport: 38083 + dahuaport: 38083 + heikaport: 38083 + haikangapp: riis-haikang + dahuaapp: riis-dahua + heikaapp: riis-heika + dockserver: # 无人机机巢服务 + ip: 192.168.1.20 + port: 6789 + app: + patrolserver: #巡视主机服务器 + ip: 192.168.1.20 + httpport: 8070 + tcpport: 10014 + udpport: 8090 + appname: + # 区域巡视主机唯一ID + serverid: 34020000002000000001 + robotid: 34020000011700000001 + dotask: true #是否执行定期任务 + snapfilepath: d:\riis\video\ #视频截图文件路径 + alarmfilepath: d:\riis\alarm\ #报警图片存储路径 + tempfilepath: d:\riis\temp\ #模板图片路径 + ffmpegpath: E:\ffmpeg\bin\ #ffmpeg截图命名路径 + modelpath: d:\riis\model\ + documentpath: d:\riis\document\ + + callmethod: 0 #热成像调用方式 0: 调用sdk, 1: 调用智能分析主机 + filefromtype: 1 # 1: 摄像机视频流截图, 2:机器人采集上传文件 3:测试检测目录文件 + testfilepath: d:\Test\ #检测识别文件存放目录 + +algorithm: + manufacturer: + - ip: 192.168.1.20 + port: 20011 + name: 海康 + id: 1 + - ip: 192.168.1.19 + port: 20012 + name: 大华 + id: 2 + - ip: 192.168.1.19 + port: 20013 + name: 黑卡 + id: 3 +#上级系统tcp服务配置 +parentserver: + serverip: 192.168.1.119 + serverid: 34020000012000000001 + tcpport: 10011 + ftpport: 2121 + ftpusername: admin + ftppassword: admin + +#算法管理平台服务配置 +algorithmserver: + serverip: 192.168.1.20 + ftpport: 2121 + ftpusername: admin + ftppassword: admin + httpport: 8090 + username: admin + password: 123456 + +mqttserver: + username: admin # 账号 + password: public # 密码 + host-url: tcp://82.156.18.154:1883 # mqtt连接tcp地址 + in-client-id: mqtt_client_patrolhost_001 # 随机值,使出入站 client ID 不同 + out-client-id: mqtt_client_parenthost_002 + client-id: ${random.int} # 客户端Id,不能相同,采用随机数 ${random.value} + default-topic: nodes/java/user/# # 默认主题-智巡系统 + timeout: 60 # 超时时间 + keepalive: 60 # 保持连接 + clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) + + + +#告警模板 +alarmtemplate: + #安全帽 + helmetcamera: '%s-%s的%s在%s,未戴安全帽的置信度为%s,判定为[%s]告警!' + #工服 + clothcamera: '%s-%s的%s在%s,未穿工服的置信度为%s,判定为[%s]告警!' + #人员闯入 + person_enter: '%s-%s的%s在%s,人员闯入了设定区域,置信度为%s,判定为[%s]告警!' + #人员聚集 + intensivecamera: '%s-%s的%s在%s,人员聚集的识别人数为%s,判定为[%s]告警!' + #烟火 + firecamera: '%s-%s的%s在%s,发生烟火的置信度为%s,判定为[%s]告警!' + #渗漏油 + leakagecamera: '%s-%s的%s在%s,发生渗漏油的置信度为%s,判定为[%s]告警!' + #渗漏油 + person_smoke: '%s-%s的%s在%s,人员吸烟的置信度为%s,判定为[%s]告警!' + +file-space: #项目文档空间 + files: d:\riis\files\ #单独上传的文件附件 + system: d:\riis\system\ #单独上传的文件 + confirmfilepath: d:\riis\confirm\ #一键顺控反馈确认文件路径 +current-info: + version: "20240102" +task: + pool: + # 核心线程池大小 + core-pool-size: 100 + # 最大线程数 + max-pool-size: 3000 + # 活跃时间 + keep-alive-seconds: 60 + # 队列容量 + queue-capacity: 50 + +sntp: + server-ip: ntp1.aliyun.com + server-port: 123 + +middle: + enable: true + #20个中台接口 + crud: + platform: http://127.0.0.1:41009 + tenantId: f6a37f99af0511ecb85bfa163ec6349c + tenantKey: MgIMP2WZfL3n5CY5NSM7hSQQVjAbljFwotiLzSL+lJYWSadcX0we2wffjyTUYGsK + +# 腾讯云短信服务 +tencent: + sms: + secret-id: "AKIDCICsLxKSM4Zq6QZVFl2745yeIwVxHRXk" + secret-key: "84oNix8rU6lJtaBfx2yoY5VimhYUk1CW" + sdk-app-id: 1400875404 + sign-name: "天宏博科技" + # 模板ID映射,key任意设置,value为审核通过模板ID + template-id-map: + register: "2015902" + login: "2018010" + forgot: "2018723" + # 以下两项非必填 +# senderid: "" +# extend-code: "" diff --git a/riis-system/src/main/resources/application-prod.yml b/riis-system/src/main/resources/application-prod.yml new file mode 100644 index 0000000..ea1cb08 --- /dev/null +++ b/riis-system/src/main/resources/application-prod.yml @@ -0,0 +1,267 @@ +server: + port: 8070 + appname: + +spring: + #应用名称 + application: + name: Project-plateform + datasource: + type: com.alibaba.druid.pool.DruidDataSource + druid: + master: + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.66.2:3306/riisdb500_zhuangzhou?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true + username: root + password: ylfw20230626@ #ylfw20230626@ #ylfw20230626@ + #initial-size: 5 + # max-active: 10 + #max-wait: 20000 # 最大等待时间,单位毫秒 + #min-idle: 2 + #time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + #min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒 + backuppath: d:\riis\databackup\ + + # REDIS数据库配置 + redis: + # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 + #host: 127.0.0.1 #82.156.18.154 + host: 192.168.66.2 + # [必须修改] 端口号 + port: 6379 + # [可选] 数据库 DB + database: 0 + # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 + # password: ZGHszy741230 + # [可选] 超时时间 + timeout: 1000000 + # mysql数据源 + + mvc: + pathmatch: + matching-strategy: ant_path_matcher + servlet: + multipart: + max-file-size: 100MB + max-request-size: 200MB +# mail: +# #smtp服务主机 qq邮箱则为smtp.qq.com +# host: smtp.qq.com +# #服务协议 +# protocol: smtp +# # 编码集 +# default-encoding: UTF-8 +# #发送邮件的账户 +# username: 1750840654@qq.com +# #授权码 +# password: nwsefzydtnwuehcd +# test-connection: true +# properties: +# mail: +# smtp: +# auth: true +# starttls: +# enable: true +# required: true +### 特殊情况下,查询结果返回List格式,会自动将查询结果为null的字段忽略,加入该配置则不会忽略 +mybatis-plus: + configuration: + call-setters-on-nulls: true + +logging: + file: + name: logs/projectname.log + level: + com.genersoft.iot: debug + com.genersoft.iot.vmp.storager.dao: info + com.genersoft.iot.vmp.gb28181: info + +# 在线文档: swagger-ui(生产环境建议关闭) +swagger-ui: + enabled: true + +# 登录相关配置 +login: + # 登录缓存 + cache-enable: true + # 是否限制单用户登录 + single-login: false + # 验证码 + login-code: + # 验证码类型配置 查看 LoginProperties 类 + code-type: arithmetic + # 登录图形验证码有效时间/分钟 + expiration: 2 + # 验证码高度 + width: 111 + # 验证码宽度 + heigth: 36 + # 内容长度 + length: 2 + # 字体名称,为空则使用默认字体 + font-name: + # 字体大小 + font-size: 25 +# IP 本地解析 +ip: + local-parsing: true + +httpserver: #配置http请求访问的地址 + monitorserver: #视频监控服务器(与边缘节点同一IP) + ip: 192.168.66.2 + port: 8080 + rtspport: 554 + appname: + + meidiaserver: #流媒体服务器(与边缘节点同一IP) + ip: 192.168.66.2 + port: 81 + secret: QUdZ1Gk4axB57kVvn0UKnzdGGHvpROvI + + analyseserver: #智能分析主机服务器 + ip: 192.168.66.2 + port11: 20011 #分析调用端口 + port12: 20011 #静默监视任务端口 + port13: 20013 #算法更新端口 + callanalyse: true + sdkserver: #第三方sdk服务 + ip: 192.168.66.2 + haikangport: 38083 + dahuaport: 38083 + heikaport: 38083 + haikangapp: riis-haikang + dahuaapp: riis-dahua + heikaapp: riis-heika + dockserver: # 无人机机巢服务 + ip: 192.168.66.2 + port: 6789 + app: + patrolserver: #巡视主机服务器 + ip: 192.168.66.2 + httpport: 8080 + tcpport: 10014 + udpport: 8090 + appname: + # 区域巡视主机唯一ID + serverid: 34020000002000000001 + robotid: 34020000011700000001 + dotask: true #是否执行定期任务 + snapfilepath: d:\riis\video\ #视频截图文件路径 + alarmfilepath: d:\riis\alarm\ #报警图片存储路径 + tempfilepath: d:\riis\temp\ #模板图片路径 + ffmpegpath: E:\ffmpeg\bin\ #ffmpeg截图命名路径 + modelpath: d:\riis\model\ + documentpath: d:\riis\document\ + + callmethod: 0 #热成像调用方式 0: 调用sdk, 1: 调用智能分析主机 + filefromtype: 1 # 1: 摄像机视频流截图, 2:机器人采集上传文件 3:测试检测目录文件 + testfilepath: d:\Test\ #检测识别文件存放目录 + +algorithm: + manufacturer: + - ip: 192.168.1.101 + port: 20011 + name: 海康 + id: 1 + - ip: 192.168.1.19 + port: 20012 + name: 大华 + id: 2 + - ip: 192.168.1.19 + port: 20013 + name: 黑卡 + id: 3 +#上级系统tcp服务配置 +parentserver: + serverip: 192.168.1.119 + serverid: 34020000012000000001 + tcpport: 10011 + ftpport: 2121 + ftpusername: admin + ftppassword: admin + +#算法管理平台服务配置 +algorithmserver: + serverip: 192.168.1.20 + ftpport: 2121 + ftpusername: admin + ftppassword: admin + httpport: 8090 + username: admin + password: 123456 + +mqttserver: + username: admin # 账号 + password: public # 密码 + host-url: tcp://82.156.18.154:1883 # mqtt连接tcp地址 + in-client-id: mqtt_client_patrolhost_001 # 随机值,使出入站 client ID 不同 + out-client-id: mqtt_client_parenthost_002 + client-id: ${random.int} # 客户端Id,不能相同,采用随机数 ${random.value} + default-topic: nodes/java/user/# # 默认主题-智巡系统 + timeout: 60 # 超时时间 + keepalive: 60 # 保持连接 + clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) + + + +#告警模板 +alarmtemplate: + #安全帽 + helmetcamera: '%s-%s的%s在%s,未戴安全帽的置信度为%s,判定为[%s]告警!' + #工服 + clothcamera: '%s-%s的%s在%s,未穿工服的置信度为%s,判定为[%s]告警!' + #人员闯入 + person_enter: '%s-%s的%s在%s,人员闯入了设定区域,置信度为%s,判定为[%s]告警!' + #人员聚集 + intensivecamera: '%s-%s的%s在%s,人员聚集的识别人数为%s,判定为[%s]告警!' + #烟火 + firecamera: '%s-%s的%s在%s,发生烟火的置信度为%s,判定为[%s]告警!' + #渗漏油 + leakagecamera: '%s-%s的%s在%s,发生渗漏油的置信度为%s,判定为[%s]告警!' + #渗漏油 + person_smoke: '%s-%s的%s在%s,人员吸烟的置信度为%s,判定为[%s]告警!' + +file-space: #项目文档空间 + files: d:\riis\files\ #单独上传的文件附件 + system: d:\riis\system\ #单独上传的文件 + confirmfilepath: d:\riis\confirm\ #一键顺控反馈确认文件路径 +current-info: + version: "20240102" +task: + pool: + # 核心线程池大小 + core-pool-size: 100 + # 最大线程数 + max-pool-size: 3000 + # 活跃时间 + keep-alive-seconds: 60 + # 队列容量 + queue-capacity: 50 + +sntp: + server-ip: ntp1.aliyun.com + server-port: 123 + +middle: + enable: true + #20个中台接口 + crud: + platform: http://127.0.0.1:41009 + tenantId: f6a37f99af0511ecb85bfa163ec6349c + tenantKey: MgIMP2WZfL3n5CY5NSM7hSQQVjAbljFwotiLzSL+lJYWSadcX0we2wffjyTUYGsK + +# 腾讯云短信服务 +tencent: + sms: + secret-id: "AKIDCICsLxKSM4Zq6QZVFl2745yeIwVxHRXk" + secret-key: "84oNix8rU6lJtaBfx2yoY5VimhYUk1CW" + sdk-app-id: 1400875404 + sign-name: "天宏博科技" + # 模板ID映射,key任意设置,value为审核通过模板ID + template-id-map: + register: "2015902" + login: "2018010" + forgot: "2018723" + # 以下两项非必填 +# senderid: "" +# extend-code: "" diff --git a/riis-system/src/main/resources/application.yml b/riis-system/src/main/resources/application.yml new file mode 100644 index 0000000..f49bdef --- /dev/null +++ b/riis-system/src/main/resources/application.yml @@ -0,0 +1,10 @@ +spring: + profiles: + active: dev + +java: + opts: -Xmx4096m -Xms4096m + +#SM4密码加密传输,前端公钥加密,后端私钥解密 +rsa: + private_key: a8b502da70405867 diff --git a/riis-system/src/main/resources/ip2region/ip2region.db b/riis-system/src/main/resources/ip2region/ip2region.db new file mode 100644 index 0000000..e69de29 diff --git a/riis-system/src/main/resources/lib/AudioRender.dll b/riis-system/src/main/resources/lib/AudioRender.dll new file mode 100644 index 0000000..438acf5 Binary files /dev/null and b/riis-system/src/main/resources/lib/AudioRender.dll differ diff --git a/riis-system/src/main/resources/lib/ClientDemo.exe b/riis-system/src/main/resources/lib/ClientDemo.exe new file mode 100644 index 0000000..b3cf1a1 Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemo.exe differ diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/ReadMe.txt b/riis-system/src/main/resources/lib/ClientDemoDll/ReadMe.txt new file mode 100644 index 0000000..8006f36 --- /dev/null +++ b/riis-system/src/main/resources/lib/ClientDemoDll/ReadMe.txt @@ -0,0 +1 @@ +The dll in this directoryiconv.dll,libxml2.dll,zlib1.dll,calib.dllonly used in ClientDemo. \ No newline at end of file diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/calib.dll b/riis-system/src/main/resources/lib/ClientDemoDll/calib.dll new file mode 100644 index 0000000..328c63e Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemoDll/calib.dll differ diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/iconv.dll b/riis-system/src/main/resources/lib/ClientDemoDll/iconv.dll new file mode 100644 index 0000000..df71882 Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemoDll/iconv.dll differ diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/libcrypto-1_1-x64.dll b/riis-system/src/main/resources/lib/ClientDemoDll/libcrypto-1_1-x64.dll new file mode 100644 index 0000000..f2f96d7 Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemoDll/libcrypto-1_1-x64.dll differ diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/libssl-1_1-x64.dll b/riis-system/src/main/resources/lib/ClientDemoDll/libssl-1_1-x64.dll new file mode 100644 index 0000000..8034d79 Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemoDll/libssl-1_1-x64.dll differ diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/libxml2.dll b/riis-system/src/main/resources/lib/ClientDemoDll/libxml2.dll new file mode 100644 index 0000000..c8d57f3 Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemoDll/libxml2.dll differ diff --git a/riis-system/src/main/resources/lib/ClientDemoDll/zlib1.dll b/riis-system/src/main/resources/lib/ClientDemoDll/zlib1.dll new file mode 100644 index 0000000..82913fb Binary files /dev/null and b/riis-system/src/main/resources/lib/ClientDemoDll/zlib1.dll differ diff --git a/riis-system/src/main/resources/lib/GdiPlus.dll b/riis-system/src/main/resources/lib/GdiPlus.dll new file mode 100644 index 0000000..b759ed6 Binary files /dev/null and b/riis-system/src/main/resources/lib/GdiPlus.dll differ diff --git a/riis-system/src/main/resources/lib/GdiPlus.lib b/riis-system/src/main/resources/lib/GdiPlus.lib new file mode 100644 index 0000000..6fb1bb6 Binary files /dev/null and b/riis-system/src/main/resources/lib/GdiPlus.lib differ diff --git a/riis-system/src/main/resources/lib/HCCore.dll b/riis-system/src/main/resources/lib/HCCore.dll new file mode 100644 index 0000000..f03d7f3 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCCore.dll differ diff --git a/riis-system/src/main/resources/lib/HCCore.lib b/riis-system/src/main/resources/lib/HCCore.lib new file mode 100644 index 0000000..72a4b94 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCCore.lib differ diff --git a/riis-system/src/main/resources/lib/HCNetSDK.dll b/riis-system/src/main/resources/lib/HCNetSDK.dll new file mode 100644 index 0000000..2e5adb8 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDK.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDK.lib b/riis-system/src/main/resources/lib/HCNetSDK.lib new file mode 100644 index 0000000..d3996fa Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDK.lib differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/AnalyzeData.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/AnalyzeData.dll new file mode 100644 index 0000000..2988c7a Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/AnalyzeData.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/AudioIntercom.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/AudioIntercom.dll new file mode 100644 index 0000000..304030a Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/AudioIntercom.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/AudioRender.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/AudioRender.dll new file mode 100644 index 0000000..438acf5 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/AudioRender.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCAlarm.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCAlarm.dll new file mode 100644 index 0000000..d6cb588 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCAlarm.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCAlarm.lib b/riis-system/src/main/resources/lib/HCNetSDKCom/HCAlarm.lib new file mode 100644 index 0000000..add03bb Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCAlarm.lib differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCCoreDevCfg.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCCoreDevCfg.dll new file mode 100644 index 0000000..6330d92 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCCoreDevCfg.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCDisplay.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCDisplay.dll new file mode 100644 index 0000000..0620c11 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCDisplay.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCGeneralCfgMgr.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCGeneralCfgMgr.dll new file mode 100644 index 0000000..1b696a6 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCGeneralCfgMgr.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCGeneralCfgMgr.lib b/riis-system/src/main/resources/lib/HCNetSDKCom/HCGeneralCfgMgr.lib new file mode 100644 index 0000000..4d400be Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCGeneralCfgMgr.lib differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCIndustry.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCIndustry.dll new file mode 100644 index 0000000..fed0581 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCIndustry.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCPlayBack.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCPlayBack.dll new file mode 100644 index 0000000..1fe1410 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCPlayBack.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCPreview.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCPreview.dll new file mode 100644 index 0000000..9fdaf30 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCPreview.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCPreview.lib b/riis-system/src/main/resources/lib/HCNetSDKCom/HCPreview.lib new file mode 100644 index 0000000..6279d3f Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCPreview.lib differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/HCVoiceTalk.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/HCVoiceTalk.dll new file mode 100644 index 0000000..3f4204b Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/HCVoiceTalk.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/OpenAL32.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/OpenAL32.dll new file mode 100644 index 0000000..ea91a5f Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/OpenAL32.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/StreamTransClient.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/StreamTransClient.dll new file mode 100644 index 0000000..c194ed1 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/StreamTransClient.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/SystemTransform.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/SystemTransform.dll new file mode 100644 index 0000000..5a246f8 Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/SystemTransform.dll differ diff --git a/riis-system/src/main/resources/lib/HCNetSDKCom/libiconv2.dll b/riis-system/src/main/resources/lib/HCNetSDKCom/libiconv2.dll new file mode 100644 index 0000000..978e2fa Binary files /dev/null and b/riis-system/src/main/resources/lib/HCNetSDKCom/libiconv2.dll differ diff --git a/riis-system/src/main/resources/lib/HXVA.dll b/riis-system/src/main/resources/lib/HXVA.dll new file mode 100644 index 0000000..e081cb8 Binary files /dev/null and b/riis-system/src/main/resources/lib/HXVA.dll differ diff --git a/riis-system/src/main/resources/lib/HmMerge.dll b/riis-system/src/main/resources/lib/HmMerge.dll new file mode 100644 index 0000000..d46a6ac Binary files /dev/null and b/riis-system/src/main/resources/lib/HmMerge.dll differ diff --git a/riis-system/src/main/resources/lib/LocalXml.zip b/riis-system/src/main/resources/lib/LocalXml.zip new file mode 100644 index 0000000..3620e68 Binary files /dev/null and b/riis-system/src/main/resources/lib/LocalXml.zip differ diff --git a/riis-system/src/main/resources/lib/MP_Render.dll b/riis-system/src/main/resources/lib/MP_Render.dll new file mode 100644 index 0000000..e5cde63 Binary files /dev/null and b/riis-system/src/main/resources/lib/MP_Render.dll differ diff --git a/riis-system/src/main/resources/lib/NPQos.dll b/riis-system/src/main/resources/lib/NPQos.dll new file mode 100644 index 0000000..c709f56 Binary files /dev/null and b/riis-system/src/main/resources/lib/NPQos.dll differ diff --git a/riis-system/src/main/resources/lib/OpenAL32.dll b/riis-system/src/main/resources/lib/OpenAL32.dll new file mode 100644 index 0000000..ea91a5f Binary files /dev/null and b/riis-system/src/main/resources/lib/OpenAL32.dll differ diff --git a/riis-system/src/main/resources/lib/PlayCtrl.dll b/riis-system/src/main/resources/lib/PlayCtrl.dll new file mode 100644 index 0000000..dae0703 Binary files /dev/null and b/riis-system/src/main/resources/lib/PlayCtrl.dll differ diff --git a/riis-system/src/main/resources/lib/PlayCtrl.lib b/riis-system/src/main/resources/lib/PlayCtrl.lib new file mode 100644 index 0000000..b9c222c Binary files /dev/null and b/riis-system/src/main/resources/lib/PlayCtrl.lib differ diff --git a/riis-system/src/main/resources/lib/SuperRender.dll b/riis-system/src/main/resources/lib/SuperRender.dll new file mode 100644 index 0000000..1a927c7 Binary files /dev/null and b/riis-system/src/main/resources/lib/SuperRender.dll differ diff --git a/riis-system/src/main/resources/lib/YUVProcess.dll b/riis-system/src/main/resources/lib/YUVProcess.dll new file mode 100644 index 0000000..d86ec3f Binary files /dev/null and b/riis-system/src/main/resources/lib/YUVProcess.dll differ diff --git a/riis-system/src/main/resources/lib/examples.jar b/riis-system/src/main/resources/lib/examples.jar new file mode 100644 index 0000000..8f0b457 Binary files /dev/null and b/riis-system/src/main/resources/lib/examples.jar differ diff --git a/riis-system/src/main/resources/lib/hlog.dll b/riis-system/src/main/resources/lib/hlog.dll new file mode 100644 index 0000000..fe2298a Binary files /dev/null and b/riis-system/src/main/resources/lib/hlog.dll differ diff --git a/riis-system/src/main/resources/lib/hpr.dll b/riis-system/src/main/resources/lib/hpr.dll new file mode 100644 index 0000000..eef0d55 Binary files /dev/null and b/riis-system/src/main/resources/lib/hpr.dll differ diff --git a/riis-system/src/main/resources/lib/jna.jar b/riis-system/src/main/resources/lib/jna.jar new file mode 100644 index 0000000..33461ec Binary files /dev/null and b/riis-system/src/main/resources/lib/jna.jar differ diff --git a/riis-system/src/main/resources/lib/json.jar b/riis-system/src/main/resources/lib/json.jar new file mode 100644 index 0000000..2c0bf83 Binary files /dev/null and b/riis-system/src/main/resources/lib/json.jar differ diff --git a/riis-system/src/main/resources/lib/libeay32.dll b/riis-system/src/main/resources/lib/libeay32.dll new file mode 100644 index 0000000..78da508 Binary files /dev/null and b/riis-system/src/main/resources/lib/libeay32.dll differ diff --git a/riis-system/src/main/resources/lib/libmmd.dll b/riis-system/src/main/resources/lib/libmmd.dll new file mode 100644 index 0000000..8becb5e Binary files /dev/null and b/riis-system/src/main/resources/lib/libmmd.dll differ diff --git a/riis-system/src/main/resources/lib/ssleay32.dll b/riis-system/src/main/resources/lib/ssleay32.dll new file mode 100644 index 0000000..1a45a31 Binary files /dev/null and b/riis-system/src/main/resources/lib/ssleay32.dll differ diff --git a/riis-system/src/main/resources/lib/zlib1.dll b/riis-system/src/main/resources/lib/zlib1.dll new file mode 100644 index 0000000..2efbd7a Binary files /dev/null and b/riis-system/src/main/resources/lib/zlib1.dll differ diff --git a/riis-system/src/main/resources/log4jdbc.log4j2.properties b/riis-system/src/main/resources/log4jdbc.log4j2.properties new file mode 100644 index 0000000..302525f --- /dev/null +++ b/riis-system/src/main/resources/log4jdbc.log4j2.properties @@ -0,0 +1,4 @@ +# If you use SLF4J. First, you need to tell log4jdbc-log4j2 that you want to use the SLF4J logger +log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator +log4jdbc.auto.load.popular.drivers=false +log4jdbc.drivers=com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/riis-system/src/main/resources/logback.xml b/riis-system/src/main/resources/logback.xml new file mode 100644 index 0000000..1a8b55e --- /dev/null +++ b/riis-system/src/main/resources/logback.xml @@ -0,0 +1,45 @@ + + + yfAdmin + + + + + + + ${log.pattern} + ${log.charset} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/AlarmLogMapper.xml b/riis-system/src/main/resources/mapper/basedata/AlarmLogMapper.xml new file mode 100644 index 0000000..d2a59b8 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/AlarmLogMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/AlgorithmManufacturerVersionMapper.xml b/riis-system/src/main/resources/mapper/basedata/AlgorithmManufacturerVersionMapper.xml new file mode 100644 index 0000000..40e84d0 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/AlgorithmManufacturerVersionMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/AlgorithmModelMapper.xml b/riis-system/src/main/resources/mapper/basedata/AlgorithmModelMapper.xml new file mode 100644 index 0000000..ad21e4f --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/AlgorithmModelMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/DeviceChannelMapper.xml b/riis-system/src/main/resources/mapper/basedata/DeviceChannelMapper.xml new file mode 100644 index 0000000..74219e6 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/DeviceChannelMapper.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/LinkageSignalMapper.xml b/riis-system/src/main/resources/mapper/basedata/LinkageSignalMapper.xml new file mode 100644 index 0000000..524a295 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/LinkageSignalMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/PatroldeviceResumeMapper.xml b/riis-system/src/main/resources/mapper/basedata/PatroldeviceResumeMapper.xml new file mode 100644 index 0000000..763f6e5 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/PatroldeviceResumeMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SilentClothCameraMapper.xml b/riis-system/src/main/resources/mapper/basedata/SilentClothCameraMapper.xml new file mode 100644 index 0000000..e8685bd --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SilentClothCameraMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SilentFireCameraMapper.xml b/riis-system/src/main/resources/mapper/basedata/SilentFireCameraMapper.xml new file mode 100644 index 0000000..77623d4 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SilentFireCameraMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SilentHelmetCameraMapper.xml b/riis-system/src/main/resources/mapper/basedata/SilentHelmetCameraMapper.xml new file mode 100644 index 0000000..9cd78fa --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SilentHelmetCameraMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SilentIntensiveCameraMapper.xml b/riis-system/src/main/resources/mapper/basedata/SilentIntensiveCameraMapper.xml new file mode 100644 index 0000000..9155181 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SilentIntensiveCameraMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SilentLeakageCameraMapper.xml b/riis-system/src/main/resources/mapper/basedata/SilentLeakageCameraMapper.xml new file mode 100644 index 0000000..4c3bb37 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SilentLeakageCameraMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationAreaMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationAreaMapper.xml new file mode 100644 index 0000000..8cf1ddb --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationAreaMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationBayMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationBayMapper.xml new file mode 100644 index 0000000..54f03d4 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationBayMapper.xml @@ -0,0 +1,37 @@ + + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationComponentMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationComponentMapper.xml new file mode 100644 index 0000000..69033df --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationComponentMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationDeviceMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationDeviceMapper.xml new file mode 100644 index 0000000..4df71ef --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationDeviceMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationMaindeviceMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationMaindeviceMapper.xml new file mode 100644 index 0000000..6f355d5 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationMaindeviceMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationMapper.xml new file mode 100644 index 0000000..040299c --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationModelMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationModelMapper.xml new file mode 100644 index 0000000..7e0761e --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationModelMapper.xml @@ -0,0 +1,4 @@ + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/SubstationPatroldeviceMapper.xml b/riis-system/src/main/resources/mapper/basedata/SubstationPatroldeviceMapper.xml new file mode 100644 index 0000000..d6716df --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/SubstationPatroldeviceMapper.xml @@ -0,0 +1,96 @@ + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/basedata/WeatherLogMapper.xml b/riis-system/src/main/resources/mapper/basedata/WeatherLogMapper.xml new file mode 100644 index 0000000..ab9e110 --- /dev/null +++ b/riis-system/src/main/resources/mapper/basedata/WeatherLogMapper.xml @@ -0,0 +1,13 @@ + + + + + + DELETE + FROM + iis_weather_log + WHERE + DATE( time ) > DATE_SUB( CURDATE(), INTERVAL 1 DAY ) + AND datastatus = '0' + + diff --git a/riis-system/src/main/resources/mapper/patroltask/AlarmLogMapper.xml b/riis-system/src/main/resources/mapper/patroltask/AlarmLogMapper.xml new file mode 100644 index 0000000..8646a29 --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/AlarmLogMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/patroltask/DefectDeviceMapper.xml b/riis-system/src/main/resources/mapper/patroltask/DefectDeviceMapper.xml new file mode 100644 index 0000000..da37735 --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/DefectDeviceMapper.xml @@ -0,0 +1,53 @@ + + + + + + diff --git a/riis-system/src/main/resources/mapper/patroltask/ExaminePlanMapper.xml b/riis-system/src/main/resources/mapper/patroltask/ExaminePlanMapper.xml new file mode 100644 index 0000000..b26840f --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/ExaminePlanMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/patroltask/TaskDologMapper.xml b/riis-system/src/main/resources/mapper/patroltask/TaskDologMapper.xml new file mode 100644 index 0000000..0996f7d --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/TaskDologMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/patroltask/TaskMapper.xml b/riis-system/src/main/resources/mapper/patroltask/TaskMapper.xml new file mode 100644 index 0000000..c337ccd --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/TaskMapper.xml @@ -0,0 +1,31 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/patroltask/TaskResultMapper.xml b/riis-system/src/main/resources/mapper/patroltask/TaskResultMapper.xml new file mode 100644 index 0000000..5340943 --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/TaskResultMapper.xml @@ -0,0 +1,778 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/patroltask/TaskTodoMapper.xml b/riis-system/src/main/resources/mapper/patroltask/TaskTodoMapper.xml new file mode 100644 index 0000000..8301deb --- /dev/null +++ b/riis-system/src/main/resources/mapper/patroltask/TaskTodoMapper.xml @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/robotapi/PlatformParentSystemMapper.xml b/riis-system/src/main/resources/mapper/robotapi/PlatformParentSystemMapper.xml new file mode 100644 index 0000000..3d30083 --- /dev/null +++ b/riis-system/src/main/resources/mapper/robotapi/PlatformParentSystemMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/robotapi/RobotOfflineLogMapper.xml b/riis-system/src/main/resources/mapper/robotapi/RobotOfflineLogMapper.xml new file mode 100644 index 0000000..73fa4a6 --- /dev/null +++ b/riis-system/src/main/resources/mapper/robotapi/RobotOfflineLogMapper.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/robotapi/RobotRunLogMapper.xml b/riis-system/src/main/resources/mapper/robotapi/RobotRunLogMapper.xml new file mode 100644 index 0000000..040484b --- /dev/null +++ b/riis-system/src/main/resources/mapper/robotapi/RobotRunLogMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/stationnode/StationnodeTranslogMapper.xml b/riis-system/src/main/resources/mapper/stationnode/StationnodeTranslogMapper.xml new file mode 100644 index 0000000..013ed2b --- /dev/null +++ b/riis-system/src/main/resources/mapper/stationnode/StationnodeTranslogMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/DictionaryMapper.xml b/riis-system/src/main/resources/mapper/system/DictionaryMapper.xml new file mode 100644 index 0000000..16edd1e --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/DictionaryMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/MessageMapper.xml b/riis-system/src/main/resources/mapper/system/MessageMapper.xml new file mode 100644 index 0000000..96cbcfc --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/MessageMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/Model3dMapper.xml b/riis-system/src/main/resources/mapper/system/Model3dMapper.xml new file mode 100644 index 0000000..d935f68 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/Model3dMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/QuartzJobMapper.xml b/riis-system/src/main/resources/mapper/system/QuartzJobMapper.xml new file mode 100644 index 0000000..b523b0a --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/QuartzJobMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/RolePatroldeviceMapper.xml b/riis-system/src/main/resources/mapper/system/RolePatroldeviceMapper.xml new file mode 100644 index 0000000..df3f05f --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/RolePatroldeviceMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysConfigMapper.xml b/riis-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..cf22aa4 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysDataBackupMapper.xml b/riis-system/src/main/resources/mapper/system/SysDataBackupMapper.xml new file mode 100644 index 0000000..f4299e8 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysDataBackupMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysDictionaryItemsMapper.xml b/riis-system/src/main/resources/mapper/system/SysDictionaryItemsMapper.xml new file mode 100644 index 0000000..1bf0942 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysDictionaryItemsMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysDictionaryMapper.xml b/riis-system/src/main/resources/mapper/system/SysDictionaryMapper.xml new file mode 100644 index 0000000..6963e40 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysDictionaryMapper.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysLogMapper.xml b/riis-system/src/main/resources/mapper/system/SysLogMapper.xml new file mode 100644 index 0000000..1046de9 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysLogMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysMenuMapper.xml b/riis-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..a025755 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,130 @@ + + + + + + update sys_menu set orderno=orderno+1 where parentid=#{parentid} and orderno < #{Orderno} and orderno >= #{upOrderno} + + + + + update sys_menu SET orderno=orderno-1 where parentid=#{parentid} and orderno > #{Orderno} and orderno <= #{downOrderno} + + + + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysMessageMapper.xml b/riis-system/src/main/resources/mapper/system/SysMessageMapper.xml new file mode 100644 index 0000000..8e540de --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysMessageMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysOrganizationMapper.xml b/riis-system/src/main/resources/mapper/system/SysOrganizationMapper.xml new file mode 100644 index 0000000..053c5ae --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysOrganizationMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysQuartzJobMapper.xml b/riis-system/src/main/resources/mapper/system/SysQuartzJobMapper.xml new file mode 100644 index 0000000..6a47db8 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysQuartzJobMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/riis-system/src/main/resources/mapper/system/SysRoleMapper.xml b/riis-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..4b37516 --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,116 @@ + + + + + INSERT INTO sys_role_menu + + + id, + + + roleid, + + + menuid + + + + + #{id}, + + + #{roleid}, + + + #{menuid} + + + + + + + + + + + + + + + + + + + + delete from sys_role_users where userid !=(select u.id from sys_user u where u.account="admin") and roleid=#{roleid} and userid=#{urserid} + + + + + DELETE FROM sys_role_menu WHERE roleid= #{id} + + + + + DELETE FROM sys_role_users WHERE roleid= #{id} + + diff --git a/riis-system/src/main/resources/mapper/system/SysUserMapper.xml b/riis-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..35c9a5b --- /dev/null +++ b/riis-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + insert into sys_role_users value (#{id},#{roleid},#{userid}) + + + + + + + + + + + + + + + + + + + + + delete from sys_role_users where userid=#{userid} + + + + delete from sys_role_users + where + userid=#{userid} + and roleid not in + + #{roleids} + + + + DELETE FROM sys_role_users WHERE userid IN + + #{id} + + + + diff --git a/riis-system/src/main/resources/static/img/1667207086203.png b/riis-system/src/main/resources/static/img/1667207086203.png new file mode 100644 index 0000000..b0f7171 Binary files /dev/null and b/riis-system/src/main/resources/static/img/1667207086203.png differ diff --git a/riis-system/src/main/resources/templates/巡视报告模板.docx b/riis-system/src/main/resources/templates/巡视报告模板.docx new file mode 100644 index 0000000..822f0c0 Binary files /dev/null and b/riis-system/src/main/resources/templates/巡视报告模板.docx differ diff --git a/riis-system/src/main/resources/win32-x86-64/AudioRender.dll b/riis-system/src/main/resources/win32-x86-64/AudioRender.dll new file mode 100644 index 0000000..a419da4 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/AudioRender.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCCore.dll b/riis-system/src/main/resources/win32-x86-64/HCCore.dll new file mode 100644 index 0000000..f03d7f3 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCCore.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDK.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDK.dll new file mode 100644 index 0000000..2e5adb8 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDK.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AnalyzeData.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AnalyzeData.dll new file mode 100644 index 0000000..2988c7a Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AnalyzeData.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AudioIntercom.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AudioIntercom.dll new file mode 100644 index 0000000..304030a Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AudioIntercom.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AudioRender.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AudioRender.dll new file mode 100644 index 0000000..438acf5 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/AudioRender.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.dll new file mode 100644 index 0000000..d6cb588 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.lib b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.lib new file mode 100644 index 0000000..add03bb Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCAlarm.lib differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCCoreDevCfg.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCCoreDevCfg.dll new file mode 100644 index 0000000..6330d92 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCCoreDevCfg.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCDisplay.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCDisplay.dll new file mode 100644 index 0000000..0620c11 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCDisplay.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.dll new file mode 100644 index 0000000..1b696a6 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.lib b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.lib new file mode 100644 index 0000000..4d400be Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCGeneralCfgMgr.lib differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCIndustry.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCIndustry.dll new file mode 100644 index 0000000..fed0581 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCIndustry.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPlayBack.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPlayBack.dll new file mode 100644 index 0000000..1fe1410 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPlayBack.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.dll new file mode 100644 index 0000000..9fdaf30 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.lib b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.lib new file mode 100644 index 0000000..6279d3f Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCPreview.lib differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCVoiceTalk.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCVoiceTalk.dll new file mode 100644 index 0000000..3f4204b Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/HCVoiceTalk.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/OpenAL32.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/OpenAL32.dll new file mode 100644 index 0000000..ea91a5f Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/OpenAL32.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/StreamTransClient.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/StreamTransClient.dll new file mode 100644 index 0000000..c194ed1 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/StreamTransClient.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/SystemTransform.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/SystemTransform.dll new file mode 100644 index 0000000..5a246f8 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/SystemTransform.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/libiconv2.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/libiconv2.dll new file mode 100644 index 0000000..978e2fa Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/libiconv2.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/msvcr90.dll b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/msvcr90.dll new file mode 100644 index 0000000..072bc0b Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HCNetSDKCom/msvcr90.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HKNetDeviceSDK.dll b/riis-system/src/main/resources/win32-x86-64/HKNetDeviceSDK.dll new file mode 100644 index 0000000..8da84cc Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HKNetDeviceSDK.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HXVA.dll b/riis-system/src/main/resources/win32-x86-64/HXVA.dll new file mode 100644 index 0000000..e081cb8 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HXVA.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/HmMerge.dll b/riis-system/src/main/resources/win32-x86-64/HmMerge.dll new file mode 100644 index 0000000..d46a6ac Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/HmMerge.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/LocalSensorAdd.dat b/riis-system/src/main/resources/win32-x86-64/LocalSensorAdd.dat new file mode 100644 index 0000000..e69de29 diff --git a/riis-system/src/main/resources/win32-x86-64/MP_Render.dll b/riis-system/src/main/resources/win32-x86-64/MP_Render.dll new file mode 100644 index 0000000..e5cde63 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/MP_Render.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/MP_VIE.dll b/riis-system/src/main/resources/win32-x86-64/MP_VIE.dll new file mode 100644 index 0000000..078f289 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/MP_VIE.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/NPQos.dll b/riis-system/src/main/resources/win32-x86-64/NPQos.dll new file mode 100644 index 0000000..c709f56 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/NPQos.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/OpenAL32.dll b/riis-system/src/main/resources/win32-x86-64/OpenAL32.dll new file mode 100644 index 0000000..8c9daaf Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/OpenAL32.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/Packet.dll b/riis-system/src/main/resources/win32-x86-64/Packet.dll new file mode 100644 index 0000000..a8bbfa4 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/Packet.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/PlayCtrl.dll b/riis-system/src/main/resources/win32-x86-64/PlayCtrl.dll new file mode 100644 index 0000000..62efe5f Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/PlayCtrl.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/Sadp.dll b/riis-system/src/main/resources/win32-x86-64/Sadp.dll new file mode 100644 index 0000000..f89ea73 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/Sadp.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/SuperRender.dll b/riis-system/src/main/resources/win32-x86-64/SuperRender.dll new file mode 100644 index 0000000..a48cb7b Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/SuperRender.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/YUVProcess.dll b/riis-system/src/main/resources/win32-x86-64/YUVProcess.dll new file mode 100644 index 0000000..d86ec3f Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/YUVProcess.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/h/hk-net-sdk-inside.h b/riis-system/src/main/resources/win32-x86-64/h/hk-net-sdk-inside.h new file mode 100644 index 0000000..1c44e2e --- /dev/null +++ b/riis-system/src/main/resources/win32-x86-64/h/hk-net-sdk-inside.h @@ -0,0 +1,95 @@ +#ifndef _HIK_NET_SDK_INSIDE_H_ +#define _HIK_NET_SDK_INSIDE_H_ + + +#include "hk-net-sdk.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#ifndef _HK_MACHINE_RECORD_FILE_INFO_TYPE_DEF_ +#define _HK_MACHINE_RECORD_FILE_INFO_TYPE_DEF_ +struct NET_Machine_Record_info{ + unsigned long long ull_start_time; + unsigned long long ull_stop_time; + char pc_filename[64]; +}; +#endif + +HK_NET_API hk_mulcast_discovery_send_inquiry(HKHANDLE h); + + +#ifndef LIBRARY_SUPPORT_ONLY_HEIKA +#ifndef _DECODE_CALLBACK_DEF_ +#define _DECODE_CALLBACK_DEF_ +typedef bool(CALLBACK *REALTIME_VIDEO_DECODE_CB_FUNC)(unsigned int ui_channel_no, unsigned char *puc_data, unsigned int ui_len, unsigned short us_width, unsigned short us_height, void *p_user_data); +#endif + +HK_NET_API hk_net_dev_enable_other_company(); + +//ص +HK_NET_API hk_net_dev_set_realplay_decode_callback(HKHANDLE h, REALTIME_VIDEO_DECODE_CB_FUNC p_func, void *p_user); + +//(ͷֻΪ) +HK_NET_API hk_net_dev_get_len_type(HKHANDLE h, int *pi_type); + + +HK_NET_API hk_net_dev_get_all_part(HKHANDLE h, char pc_ir_ip[16], char pc_vi_ip[16], unsigned int *pui_ptz); +HK_NET_API hk_net_dev_update_machine_software(HKHANDLE h, char *pc_filename); + + +//--------------------½ӿڽ豸֧----------------- +HK_NET_API hk_net_dev_capture_all_data(HKHANDLE h, char *puc_ir_data, unsigned int *pui_ir_len, char *puc_vi_data, unsigned int *pui_vi_len, char *puc_temperature_data, unsigned int *pui_temp_len);//pc_data, 14λʱ,20210802151515 +//ģʽȡ(ںorͨ) +HK_NET_API hk_net_dev_get_pip_capabilities(HKHANDLE h, bool *b_fusion, float *f_dist_start, float *f_dist_end, float *f_dist); +HK_NET_API hk_net_dev_set_pip_capabilities(HKHANDLE h, bool b_fusion, float f_dist); + +//ֱ䱶 +HK_NET_API hk_net_dev_set_zoom_limit(HKHANDLE h, unsigned int ui_channel_no, unsigned int ui_zoom_limit); +HK_NET_API hk_net_dev_zoom_ctrl(HKHANDLE h, unsigned int ui_channel_no, int i_type); //i_type: 0, stop; 1, in; 2, out + +HK_NET_API hk_net_set_preset_temperature_alarm_upload_flag(HKHANDLE h, bool b_enable); +HK_NET_API hk_net_get_preset_temperature_alarm_upload_flag(HKHANDLE h, bool *pb_enable); + +HK_NET_API hk_net_get_ptz_absolute_zoom(HKHANDLE h, unsigned int ui_channel_no, unsigned int *pui_absolute_zoom); + +//--------------------Ͻӿڽ豸֧----------------- +#endif + +//-------------------------------------------------------------ķָ-------------------------------------------------------------------------------- + +//--------------------½ӿڽڿ豸֧----------------- +#ifndef _IMAGING_INFO_CB_DEF_ +#define _IMAGING_INFO_CB_DEF_ +typedef bool(CALLBACK *REALTIME_VIDEO_IMAGING_INFO_CB_FUNC)(unsigned int ui_channel_no, int i_type, float f_range_max, float f_range_min, void *p_user_data); +#endif + +HK_NET_API hk_net_dev_set_imaging_info_callback(HKHANDLE h, REALTIME_VIDEO_IMAGING_INFO_CB_FUNC p_func, void *p_user); +HK_NET_API hk_net_dev_set_imaging_info(HKHANDLE h, int i_type, float f_range_max, float f_range_min);//i_type: 0Զ1ֶ + +HK_NET_API hk_net_dev_temperature_correct(HKHANDLE h); + +HK_NET_API hk_net_dev_disable_max_temperature_osd(HKHANDLE h, unsigned int ui_channel_no); + +HK_NET_API hk_net_dev_set_hek_palette(HKHANDLE h, int i_palette); +HK_NET_API hk_net_dev_get_hek_palette(HKHANDLE h, int *pi_palette); + +HK_NET_API hk_net_dev_set_machine_record_start(HKHANDLE h); +HK_NET_API hk_net_dev_set_machine_record_stop(HKHANDLE h); +HK_NET_API hk_net_dev_get_machine_record_status(HKHANDLE h, bool *pb_status); +HK_NET_API hk_net_dev_get_machine_record_filelist(HKHANDLE h, unsigned int ui_start_time, unsigned int ui_end_time, NET_Machine_Record_info *pst_file_list, unsigned int *pui_count); +HK_NET_API hk_net_dev_download_machine_record_file(HKHANDLE h, char *pc_filename, char *pc_save_file); + + +//--------------------Ͻӿڽڿ豸֧----------------- + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/riis-system/src/main/resources/win32-x86-64/h/hk-net-sdk.h b/riis-system/src/main/resources/win32-x86-64/h/hk-net-sdk.h new file mode 100644 index 0000000..77718e2 --- /dev/null +++ b/riis-system/src/main/resources/win32-x86-64/h/hk-net-sdk.h @@ -0,0 +1,573 @@ +#ifndef _HIK_NET_SDK_H_ +#define _HIK_NET_SDK_H_ + +#if defined(_WIN32) || defined(_WIN64) + +#if (defined HK_NET_LIBRARY) +#define HK_NET_API __declspec(dllexport) bool __stdcall +#else +#define HK_NET_API __declspec(dllimport) bool __stdcall +#endif + +#define CALLBACK __stdcall + +#else + +#define HK_NET_API bool +#define CALLBACK + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef _HK_DEFINE_HKHANDLE_ +#define _HK_DEFINE_HKHANDLE_ + typedef void *HKHANDLE; + typedef void **PHKHANDLE; +#endif + +#ifndef _HK_DEVICE_TYPE_DEF_ +#define _HK_DEVICE_TYPE_DEF_ + enum { + DEVICE_TYPE_HKDQ = 0, + DEVICE_TYPE_HIKVISION = 1, //暂未支持 + + DEVICE_TYPE_MAX, + }; +#endif + +#ifndef _HK_SSADP_INFO_DEF_ +#define _HK_SSADP_INFO_DEF_ + typedef struct tagSADPInfo + { + int i_type; + char sseries[64]; + char szSerialNO[64]; + char szMac[64]; + char szIPv4[64]; + char szSubnetMask[64]; + char szSoftVersion[64]; + char szDSPVersion[64]; + char szRelevanceSerialNo[64]; + char szGateWay[64]; + int i_dhcp; + }SSADPINFO, *PSSADPINFO; +#endif + +#ifndef _HK_DISCOVERY_DEVICE_CB_FUNC_DEF_ +#define _HK_DISCOVERY_DEVICE_CB_FUNC_DEF_ + typedef bool(CALLBACK *DISCOVERY_DEVICE_CB_FUNC)(SSADPINFO st_dev_info, void *p_user_data); +#endif + + //广播发现接口 + HK_NET_API hk_discovery_init(PHKHANDLE p_h, DISCOVERY_DEVICE_CB_FUNC p_func, void *p_user); + HK_NET_API hk_discovery_send_inquiry(HKHANDLE h); + HK_NET_API hk_discovery_stop(PHKHANDLE p_h); + + HK_NET_API hk_discovery_modify_ip(HKHANDLE h, const char *pc_src_ip, const char *pc_passwd, bool b_dhcp, const char *pc_ip, const char *pc_netmask, const char *pc_gateway, const char *pc_dns1, const char *pc_dns2); + + //总的初始化与反初始化接口,无论设备多少只调用一次 + HK_NET_API hk_net_sdk_init(const char *pc_path);//"/usr/local/HKNetDeviceSDK" + HK_NET_API hk_net_sdk_uninit(); + + enum{ + STREAM_TYPE_MAIN = 0, + STREAM_TYPE_SUB = 1, + + STREAM_TYPE_MAX, + }; + +#ifndef _HK_REALTIME_VIDEO_CB_FUNC_DEF_ +#define _HK_REALTIME_VIDEO_CB_FUNC_DEF_ + typedef bool(CALLBACK *REALTIME_VIDEO_CB_FUNC)(unsigned int ui_channel_no, unsigned int ui_type, unsigned char *puc_data, unsigned int ui_len, void *p_user_data); +#endif + +#ifndef _HK_REALTIME_TEMPERATURE_INFO_DEF_ +#define _HK_REALTIME_TEMPERATURE_INFO_DEF_ + struct HK_STREAM_FRAME_INFO_S + { + unsigned int ui_magic_no; + unsigned int ui_header_size;//结构体长度 + unsigned int ui_stream_type;//0-h264,1-h265,2-jpeg,3-aud,4-meta,5-update,6-rtdata + unsigned int ui_stream_len;//数据长度 + + //rt data info + unsigned int ui_rt_data_type;//1-14bit裸数据,2-全屏测温结果数据,3-YUV数据 + unsigned int ui_frame_no; + unsigned int ui_std_stamp;//DSP相对时间戳 + unsigned short us_year; + unsigned short us_month; + unsigned short us_dayofweek;//0-周日,6-周六 + unsigned short us_day; + unsigned short us_hour; + unsigned short us_minute; + unsigned short us_second; + unsigned short us_millisecond; + unsigned int ui_width; + unsigned int ui_height; + unsigned int ui_len; + unsigned int ui_fps; + unsigned int ui_chan; + + //fs support info + unsigned int ui_tm_data_mode;//4字节 or 2字节 + unsigned int ui_tm_scale;//测温缩放比例 + unsigned int ui_tm_offset;//测温偏移量 + unsigned int ui_tm_res; + + unsigned int ui_res[12]; + unsigned int uc_crc_val;//crc校验码 + }; +#endif + +#ifndef _HK_REALTIME_TEMPERATURE_STREAM_CB_FUNC_DEF_ +#define _HK_REALTIME_TEMPERATURE_STREAM_CB_FUNC_DEF_ + typedef bool(CALLBACK *REALTIME_TEMPERATURE_STREAM_CB_FUNC)(HK_STREAM_FRAME_INFO_S st_frame_info, unsigned char *puc_data, void *p_user_data); +#endif + + //设备操作相关接口 + //初始化,反初始化 + HK_NET_API hk_net_dev_init(PHKHANDLE p_h); + + //指定初始化,不用再调用以下接口 + //hk_net_dev_login + //hk_net_dev_set_realplay_callback + //hk_net_dev_start_realplay + //hk_net_dev_set_temperature_stream_callback + //hk_net_dev_start_temperature_stream_realplay + HK_NET_API hk_net_dev_init_ex(PHKHANDLE p_h, int i_type, const char *pc_ip, unsigned int port, const char *pc_mac, const char *pc_sn, const char *pc_user, const char *pc_pwd, + REALTIME_VIDEO_CB_FUNC p_vs_func, void *p_vs_user, REALTIME_TEMPERATURE_STREAM_CB_FUNC p_ts_func, void *p_ts_user ); + + HK_NET_API hk_net_dev_uninit(PHKHANDLE p_h); + +#ifndef _HK_EXCEPTION_CB_FUNC_DEF_ +#define _HK_EXCEPTION_CB_FUNC_DEF_ + //ui_type: 1-网络预览异常, 2-预览时重连, 3-预览时重连成功 + typedef bool(CALLBACK *EXCEPTION_CB_FUNC)(unsigned int ui_type, unsigned int lhandle, void *p_user_data); +#endif + HK_NET_API hk_net_dev_set_exception_callback(HKHANDLE h, EXCEPTION_CB_FUNC p_func, void *p_user); + + //登陆登出接口 + HK_NET_API hk_net_dev_login(HKHANDLE h, int i_type, const char *pc_ip, unsigned int port, const char *pc_mac, const char *pc_sn, const char *pc_user, const char *pc_pwd); + HK_NET_API hk_net_dev_login_nvr(HKHANDLE h, const char *pc_ip, unsigned int port, const char *pc_mac, const char *pc_sn, const char *pc_user, const char *pc_pwd); + HK_NET_API hk_net_dev_logout(HKHANDLE h); + HK_NET_API hk_net_dev_get_sn(HKHANDLE h, char *pc_sn, int *ui_len); + + HK_NET_API hk_net_dev_get_cfg_version(HKHANDLE h, char pc_dev_type[64], char pc_software_version[64], char pc_system_version[64], char pc_hardware_version[64]); + + //IP设置 + HK_NET_API hk_net_dev_set_ip(HKHANDLE h, bool b_dhcp, char *pc_ip, char *pc_netmask, char *pc_gateway, char *pc_dns1, char *pc_dns2); + HK_NET_API hk_net_dev_get_ip(HKHANDLE h, bool *b_dhcp, char *pc_ip, char *pc_netmask, char *pc_gateway, char *pc_dns1, char *pc_dns2); + //重启 + HK_NET_API hk_net_dev_reboot(HKHANDLE h); + + //时间设置 + HK_NET_API hk_net_dev_set_time(HKHANDLE h, unsigned int ui_year, unsigned int ui_month, unsigned int ui_day, unsigned int ui_hour, unsigned int ui_mintue, unsigned int ui_second); + +#ifndef _HK_VIDEO_RESOLUTION_INFO_DEF_ +#define _HK_VIDEO_RESOLUTION_INFO_DEF_ + struct video_resolution{ + unsigned short us_width; + unsigned short us_height; + }; +#endif + //支持的分辨率列表 + HK_NET_API hk_net_dev_get_support_resolutions(HKHANDLE h, unsigned int channel, int i_stream_type, struct video_resolution result[10], int *p_num);//i_stream_type: STREAM_TYPE_MAIN/STREAM_TYPE_SUB + //分辨率设置 + HK_NET_API hk_net_dev_set_resolution(HKHANDLE h, unsigned int channel, int i_stream_type, unsigned short us_width, unsigned short us_height);//i_stream_type: STREAM_TYPE_MAIN/STREAM_TYPE_SUB + //主码流分辨率获取 + HK_NET_API hk_net_dev_get_resolution(HKHANDLE h, unsigned int channel, unsigned short *pus_width, unsigned short *pus_height); + HK_NET_API hk_net_dev_get_channel_num(HKHANDLE h, unsigned int *pui_channel_num, unsigned int *pui_start_channel_num, unsigned int *pui_ip_channel_num, unsigned int *pui_start_ip_channel_num); + + //获取温度矩阵宽高 + HK_NET_API hk_net_dev_get_temperature_resolution(HKHANDLE h, unsigned short *pus_width, unsigned short *pus_height); + + //获取红外/可见光实时图像 + HK_NET_API hk_net_dev_set_realplay_callback(HKHANDLE h, REALTIME_VIDEO_CB_FUNC p_func, void *p_user); + HK_NET_API hk_net_dev_start_realplay(HKHANDLE h, unsigned int ui_channel_no, int i_stream_type);//i_stream_type: STREAM_TYPE_MAIN/STREAM_TYPE_SUB + HK_NET_API hk_net_dev_stop_transferdata(HKHANDLE h, unsigned int ui_channel_no); + HK_NET_API hk_net_dev_stop_realplay(HKHANDLE h, unsigned int ui_channel_no); + +#ifndef _HK_REALTIME_TEMPERATURE_RULE_DEF_ +#define _HK_REALTIME_TEMPERATURE_RULE_DEF_ + struct STRU_ANA_INFO { + char pc_ana[64]; + }; +#endif + +#ifndef _HK_LOCAL_TEMPERATURE_INFO_DEF_ +#define _HK_LOCAL_TEMPERATURE_INFO_DEF_ + typedef struct STRU_TEMPERTURE_INFO + { + int iType; //0, point, 1 rect, 2 line + float fMax; + int fMaxX; + int fMaxY; + + float fMin; + int fMinX; + int fMinY; + + float fAvg; + + float fTemp; //iType point + int fX; //iType point + int fY; //iType point + + STRU_TEMPERTURE_INFO() + { + iType = -1; + fMax = 0; + fMaxX = 0; + fMaxY = 0; + fMin = 0; + fMinX = 0; + fMinY = 0; + fAvg = 0; + fTemp = 0; + fX = 0; + fY = 0; + } + }LOCAL_TEMPERATURE_INFO; +#endif + + //实时分析及温度相关接口 +#ifndef _HK_REALTIME_TEMPERATURE_RULE_CB_FUNC_DEF_ +#define _HK_REALTIME_TEMPERATURE_RULE_CB_FUNC_DEF_ + typedef bool(CALLBACK *REALTIME_TEMPERATURE_RULE_CB_FUNC)(const char *pc_ana, LOCAL_TEMPERATURE_INFO local_info, void *p_user_data); +#endif + + //设置测温分析,此接口需hk_net_dev_start_temperature_stream_realplay接口调用成功并获取到回调数据后再调用,规则不会存储到设备中 + HK_NET_API hk_net_dev_set_temperature_rule(HKHANDLE h, unsigned short us_width, unsigned short us_height, STRU_ANA_INFO *pv_ana_info, unsigned int ui_cnt); + //HK_NET_API hk_net_dev_set_temperature_callback(HKHANDLE h, REALTIME_TEMPERATURE_RULE_CB_FUNC p_func, void *p_user); + + //抓图 + HK_NET_API hk_net_dev_capture_ir_jpg(HKHANDLE h, char *pc_name); + HK_NET_API hk_net_dev_capture_vi_jpg(HKHANDLE h, char *pc_name); + HK_NET_API hk_net_dev_capture_temperature_data(HKHANDLE h, char *puc_temperature_data, unsigned int *pui_temp_len); + + //pc_data, 14位时间,例:"20210802151515" + HK_NET_API hk_net_dev_save_ir_to_national_grid_image(HKHANDLE h, const char *pc_date, char *puc_ir_data, unsigned int ui_ir_len, const char *pc_file); + HK_NET_API hk_net_dev_append_temperature_to_national_grid_image(HKHANDLE h, const char *pc_date, const char *pc_file); + + //实时温度流 + HK_NET_API hk_net_dev_set_temperature_stream_callback(HKHANDLE h, REALTIME_TEMPERATURE_STREAM_CB_FUNC p_func, void *p_user); + HK_NET_API hk_net_dev_start_temperature_stream_realplay(HKHANDLE h); + HK_NET_API hk_net_dev_stop_temperature_stream_realplay(HKHANDLE h); + + HK_NET_API hk_net_dev_record_realdata(HKHANDLE h, unsigned int ui_channel, const char *pc_file); + HK_NET_API hk_net_dev_stop_record_realdata(HKHANDLE h, unsigned int ui_channel); + + enum{ + PALETTE_IRON2 = 0,//铁红2 + PALETTE_BLACKHOT = 1,//黑热 + PALETTE_WHITEHOT = 2,//白热 + PALETTE_RAINBOW = 3,//彩虹 + PALETTE_MAX = 4, + }; + //调色板设置 + HK_NET_API hk_net_dev_set_palette(HKHANDLE h, int i_palette); + HK_NET_API hk_net_dev_get_palette(HKHANDLE h, int *pi_palette); + + //基础参数配置,距离单位米 + HK_NET_API hk_net_dev_get_thermometry_basic_config(HKHANDLE h, float *pf_emiss, float *pf_dist); + HK_NET_API hk_net_dev_thermometry_basic_config(HKHANDLE h, float f_emiss, float f_dist); + + HK_NET_API hk_net_dev_get_all_temperature_paras(HKHANDLE h, float *pf_emiss, float *pf_dist, float *pf_env_temp, float *pf_hum); + HK_NET_API hk_net_dev_set_all_temperature_paras(HKHANDLE h, float f_emiss, float f_dist, float f_env_temp, float f_hum); + +#ifndef _HK_ANA_TEMPERATURE_DEF_ +#define _HK_ANA_TEMPERATURE_DEF_ + struct STRU_ANA_TEMPERATURE{ + float fp_max; + unsigned short us_max_x; + unsigned short us_max_y; + + float fp_min; + unsigned short us_min_x; + unsigned short us_min_y; + + float fp_avg; + }; +#endif + HK_NET_API hk_net_dev_capture_one_frame(HKHANDLE h); + HK_NET_API hk_net_dev_get_max_temperature(HKHANDLE h, float *fp_max, unsigned short *us_max_x, unsigned short *us_max_y); + HK_NET_API hk_net_dev_get_min_temperature(HKHANDLE h, float *fp_min, unsigned short *us_min_x, unsigned short *us_min_y); + HK_NET_API hk_net_dev_get_avg_temperature(HKHANDLE h, float *fp_avg); + HK_NET_API hk_net_dev_get_temperature_from_ana(HKHANDLE h, const char *pc_ana, struct STRU_ANA_TEMPERATURE *temperature ); + + //测温规则设置,会存储到设备中 + //pc_rules: R01:1,1;10,10;-L02:2,2;10,10;-P03:5,5;- + HK_NET_API hk_net_dev_del_preset_temperature_rules(HKHANDLE h, unsigned int ui_preset_no); + HK_NET_API hk_net_dev_set_preset_temperature_rules(HKHANDLE h, unsigned int ui_preset_no, float pf_emiss, float pf_dist, const char *pc_rules, unsigned int ui_rules_len); + HK_NET_API hk_net_dev_get_preset_temperature_rules(HKHANDLE h, unsigned int ui_preset_no, float *pf_emiss, float *pf_dist, char *pc_rules, unsigned int *pui_rules_len); + + //报警规则设置,会存储到设备中,报警条件的温度均按摄氏度计算,调用相关接口时先调用hk_net_dev_set_temperature_unit将测温单位切换为摄氏度。 + //pc_rules:R01:Max>10;P02:Min<5;P03:Avg>10; + HK_NET_API hk_net_dev_del_preset_temperature_alarm_rules(HKHANDLE h, unsigned int ui_preset_id); + HK_NET_API hk_net_dev_set_preset_temperature_alarm_rules(HKHANDLE h, unsigned int ui_preset_id, char *pc_rules, unsigned int ui_lens); + HK_NET_API hk_net_dev_get_preset_temperature_alarm_rules(HKHANDLE h, unsigned int ui_preset_id, char *pc_rules, unsigned int *pui_lens); + + //区域报警规则设置,会存储到设备中 + HK_NET_API hk_net_dev_del_preset_diff_temperature_alarm_rules(HKHANDLE h, unsigned int ui_preset_id); + HK_NET_API hk_net_dev_set_preset_diff_temperature_alarm_rules(HKHANDLE h, unsigned int ui_preset_id, char *pc_rules, unsigned int ui_lens); + HK_NET_API hk_net_dev_get_preset_diff_temperature_alarm_rules(HKHANDLE h, unsigned int ui_preset_id, char *pc_rules, unsigned int *pui_lens); + + //自动切换测温档相关接口 +#ifndef _HK_NET_MEAS_RANGE_ +#define _HK_NET_MEAS_RANGE_ + struct HK_NET_MEAS_RANGE{ + unsigned char uc_range_type; + float f_range_min; + float f_range_max; + }; +#endif + HK_NET_API hk_net_dev_get_temperature_meas_range(HKHANDLE h, unsigned int *pui_cnt, struct HK_NET_MEAS_RANGE *pst_range); + HK_NET_API hk_net_dev_get_curr_temperature_meas_range(HKHANDLE h, unsigned int *ui_meas_range, float *f_range_min, float *f_range_max); + HK_NET_API hk_net_dev_set_temperature_meas_range(HKHANDLE h, unsigned int ui_meas_range); + + HK_NET_API hk_net_dev_is_support_auto_change_meas(HKHANDLE h, bool *pb_auto_meas_range); + HK_NET_API hk_net_dev_is_auto_temperature_meas_range(HKHANDLE h, bool *pb_auto_meas_range); + HK_NET_API hk_net_dev_set_auto_temperature_meas_range(HKHANDLE h, bool b_auto_meas_range); + + //设置与获取测温单位 +#ifndef _HK_NET_TEMPERTATURE_UNIT_ +#define _HK_NET_TEMPERTATURE_UNIT_ + enum { + TEMPERATURE_UNIT_CELSIUS = 0,//摄氏度 + TEMPERATURE_UNIT_FAHRENHEIT,//华氏度 + + TEMPERATURE_UNIT_MAX, + }; +#endif + HK_NET_API hk_net_dev_set_temperature_unit(HKHANDLE h, int e_type); + HK_NET_API hk_net_dev_get_temperature_unit(HKHANDLE h, int* pe_type); + + //视频叠加温度配置,会存储到设备中 + HK_NET_API hk_net_dev_set_video_overlay_temperature_info(HKHANDLE h, bool b_enable); + HK_NET_API hk_net_dev_get_video_overlay_temperature_info(HKHANDLE h, bool *pb_enable); + +#ifndef _HK_OSD_CONFIG_ +#define _HK_OSD_CONFIG_ + struct STRU_OSD_CONFIG{ + unsigned char uc_show_chan;//0:不显示, 1:显示 + unsigned char puc_chan_name[32]; + unsigned short us_chan_name_x; + unsigned short us_chan_name_y; + + unsigned char uc_show_time;//0:不显示, 1:显示 + unsigned short us_time_x; + unsigned short us_time_y; + unsigned char uc_time_type;//0: XXXX-XX-XX 年月日, 1: XX-XX-XXXX 月日年, 2: XXXX年XX月XX日, 3: XX月XX日XXXX年, 4: XX-XX-XXXX 日月年, 5: XX日XX月XXXX年, 6: xx/xx/xxxx(月/日/年), 7: xxxx/xx/xx(年/月/日),8: xx/xx/xxxx(日/月/年) + unsigned char uc_show_week;//是否显示星期 0:不显示, 1:显示 + unsigned char uc_time_attrib;// 1: 透明,闪烁, 2, 透明,不闪烁, 3: 不透明,闪烁, 4: 不透明,不闪烁 + unsigned char uc_time_hour_type;//OSD小时制:0-24小时制,1-12小时制 + }; +#endif + + HK_NET_API hk_net_dev_get_osd(HKHANDLE h, unsigned int ui_channel_no, STRU_OSD_CONFIG &st_config); + HK_NET_API hk_net_dev_set_osd(HKHANDLE h, unsigned int ui_channel_no, STRU_OSD_CONFIG st_config); + + //调焦- + HK_NET_API hk_net_dev_zoom_in(HKHANDLE h, unsigned int ui_channel_no, int i_stop); + //调焦+ + HK_NET_API hk_net_dev_zoom_out(HKHANDLE h, unsigned int ui_channel_no, int i_stop); + //聚焦- + HK_NET_API hk_net_dev_focus_near(HKHANDLE h, unsigned int ui_channel_no, int i_stop); + //聚焦+ + HK_NET_API hk_net_dev_focus_far(HKHANDLE h, unsigned int ui_channel_no, int i_stop); + //光圈- + HK_NET_API hk_net_dev_iris_close(HKHANDLE h, unsigned int ui_channel_no, int i_stop); + //光圈+ + HK_NET_API hk_net_dev_iris_open(HKHANDLE h, unsigned int ui_channel_no, int i_stop); + + //开灯 + HK_NET_API hk_net_dev_open_light(HKHANDLE h); + //关灯 + HK_NET_API hk_net_dev_close_light(HKHANDLE h); + //开雨刷 + HK_NET_API hk_net_dev_open_wiper(HKHANDLE h); + //关雨刷 + HK_NET_API hk_net_dev_close_wiper(HKHANDLE h); + + enum{ + PTZ_TILT_UP = 0, //上 + PTZ_TILT_DOWN = 1,//下 + PTZ_PAN_LEFT = 2,//左 + PTZ_PAN_RIGHT = 3,//右 + PTZ_UP_LEFT = 4,//左上 + PTZ_UP_RIGHT = 5,//右上 + PTZ_DOWN_LEFT = 6,//左下 + PTZ_DOWN_RIGHT = 7,//右下 + PTZ_PAN_AUTO = 8,//左右自动扫描 + }; + HK_NET_API hk_net_dev_ptz_control(HKHANDLE h, int ptz_control_type, int i_speed, int i_stop);//i_speed: 1~7 + + //预置点设置 + HK_NET_API hk_net_dev_ptz_set_preset(HKHANDLE h, int i_preset_index); + //召回预置点 + HK_NET_API hk_net_dev_ptz_goto_preset(HKHANDLE h, int i_preset_index); + //删除预置点 + HK_NET_API hk_net_dev_ptz_del_preset(HKHANDLE h, int i_preset_index); + //清空预置点 + HK_NET_API hk_net_dev_ptz_clear_all_preset(HKHANDLE h); + +#ifndef _HK_PRESET_CONFIG_ +#define _HK_PRESET_CONFIG_ + struct STRU_PRESET_CONFIG{ + unsigned int ui_enable; //1 enable, 0 disable + unsigned int ui_especial; //1 especial, 0 normal + unsigned int ui_no; + unsigned char puc_preset_name[64]; + }; +#endif + //获取预置点列表 + HK_NET_API hk_net_dev_ptz_get_preset_list(HKHANDLE h, STRU_PRESET_CONFIG *p_preset, unsigned int *pui_count); + +#ifndef LIBRARY_SUPPORT_ONLY_HEIKA + //同步视场角 + HK_NET_API hk_net_dev_synchronzie_fov(HKHANDLE h); + +//nvr--------------------------------------------------------start + //NVR相关 +#ifndef _HK_NVR_FILE_DEF_ +#define _HK_NVR_FILE_DEF_ + struct HK_NVR_FILE_COND{ + unsigned int ui_start_year; + unsigned int ui_start_month; + unsigned int ui_start_day; + unsigned int ui_start_hour; + unsigned int ui_start_minute; + unsigned int ui_start_second; + + unsigned int ui_stop_year; + unsigned int ui_stop_month; + unsigned int ui_stop_day; + unsigned int ui_stop_hour; + unsigned int ui_stop_minute; + unsigned int ui_stop_second; + }; + + struct HK_NVR_FILE_INFO{ + char pc_file_name[128]; + unsigned int ui_file_size; + + unsigned int ui_start_year; + unsigned int ui_start_month; + unsigned int ui_start_day; + unsigned int ui_start_hour; + unsigned int ui_start_minute; + unsigned int ui_start_second; + + unsigned int ui_stop_year; + unsigned int ui_stop_month; + unsigned int ui_stop_day; + unsigned int ui_stop_hour; + unsigned int ui_stop_minute; + unsigned int ui_stop_second; + }; +#endif + +#ifndef _HK_NVR_IP_CHANNEL_ +#define _HK_NVR_IP_CHANNEL_ + struct STRU_NVR_IP_CHANNEL{ + char pc_ip[16]; + unsigned int ui_channel[2]; + }; +#endif + + //NVR相关接口仅针对海康NVR + HK_NET_API hk_net_dev_get_channels_config(HKHANDLE h, STRU_NVR_IP_CHANNEL *pst_ip_channels, unsigned int *ui_cnt); + //获取NVR文件列表 + HK_NET_API hk_net_dev_get_file_list(HKHANDLE h, unsigned int channel, HK_NVR_FILE_COND st_condtion, HK_NVR_FILE_INFO *pst_file_list, unsigned int *ui_cnt); + + //文件名回放 + HK_NET_API hk_net_dev_play_back_by_name(HKHANDLE h, char *pc_name, int *i_handle); + //时间回放 + HK_NET_API hk_net_dev_play_back_by_time(HKHANDLE h, unsigned int channel, HK_NVR_FILE_COND st_condition, int *i_handle); + + HK_NET_API hk_net_dev_play_back_stop(HKHANDLE h, int i_handle); + //回放时下载文件 + HK_NET_API hk_net_dev_play_back_save_data(HKHANDLE h, int i_handle, char *pc_name); + //回放时下载停止 + HK_NET_API hk_net_dev_play_back_stop_save(HKHANDLE h, int i_handle); + +#ifndef _HK_PLAY_BACK_PACKET_ES_INFO_ +#define _HK_PLAY_BACK_PACKET_ES_INFO_ + struct HK_PLAY_BACK_PACKET_ES_INFO{ + unsigned short us_width; //宽度 + unsigned short us_height; //调试 + unsigned long ul_time_stamp; //时间戳低位 + unsigned long ul_time_stamp_high; //时间戳高位 + unsigned short us_year; //时间 + unsigned short us_month; + unsigned short us_day; + unsigned short us_hour; + unsigned short us_minute; + unsigned short us_second; + unsigned short us_millisecond; + unsigned int ui_frame_num; //帧号 + unsigned int ui_frame_rate; //帧率 + unsigned int ui_packet_type; //包类型:0- 文件头,1- 视频I帧,2- 视频B帧, 3- 视频P帧, 10- 音频数据包, 11- 私有数据包 + unsigned int ui_packet_size; //包大小 + unsigned char *puc_packet_buffer; //缓冲区 + }; +#endif + +#ifndef _HK_PLAY_BACK_VIDEO_CB_FUNC_DEF_ +#define _HK_PLAY_BACK_VIDEO_CB_FUNC_DEF_ + typedef bool(CALLBACK *PLAY_BACK_VIDEO_CB_FUNC)(unsigned int i_handle, HK_PLAY_BACK_PACKET_ES_INFO *pst_es_info, void *p_user_data); +#endif + //回放数据帧回调 + HK_NET_API hk_net_dev_play_back_set_callback(HKHANDLE h, PLAY_BACK_VIDEO_CB_FUNC cb_func, void *p_user); + + //回放控制 + enum{ + PLAYBACK_START = 0,//开始播放 + PLAYBACK_STOP = 1,//停止播放 + PLAYBACK_PAUSE = 2,//暂停播放 + PLAYBACK_RESTART = 3,//恢复播放 + PLAYBACK_FAST = 4,//快放 + PLAYBACK_SLOW = 5,//慢放 + PLAYBACK_NORMAL = 6,//正常速度 + }; + HK_NET_API hk_net_dev_play_back_control(HKHANDLE h, unsigned int i_handle, int i_control); + + //以下所有接口在调用hk_net_dev_play_back_by_time时均不支持 + HK_NET_API hk_net_dev_play_back_set_pos(HKHANDLE h, unsigned int i_handle, int i_pos); + //获取回放总时长与总帧数 + HK_NET_API hk_net_dev_play_back_get_total_time(HKHANDLE h, unsigned int i_handle, unsigned int *pui_times); + HK_NET_API hk_net_dev_play_back_get_total_frames(HKHANDLE h, unsigned int i_handle, unsigned int *pui_frames); + + //获取回放当前进度相关接口,通过回调方式以下四个接口会return false + HK_NET_API hk_net_dev_play_back_control_seek_time(HKHANDLE h, unsigned int i_handle, int i_year, int i_month, int i_day, int i_hour, int i_mintue, int i_second); + HK_NET_API hk_net_dev_play_back_get_time(HKHANDLE h, unsigned int i_handle, unsigned int *pui_time); + HK_NET_API hk_net_dev_play_back_get_frame(HKHANDLE h, unsigned int i_handle, unsigned int *pui_frame); + HK_NET_API hk_net_dev_play_back_get_pos(HKHANDLE h, unsigned int i_handle, unsigned int *pui_pos); +//nvr--------------------------------------------------------end +#endif + + + +#ifndef LIBRARY_SUPPORT_ONLY_HEIKA + //判断是否支持调焦 + HK_NET_API hk_net_dev_get_len_adjust_method(HKHANDLE h, unsigned int *pui_method); + HK_NET_API hk_net_dev_set_len_pos(HKHANDLE h, unsigned int ui_len_pos); + HK_NET_API hk_net_dev_get_len_pos(HKHANDLE h, unsigned int *pui_len_pos); + + //自动聚集 + HK_NET_API hk_net_dev_auto_focus(HKHANDLE h, unsigned int ui_channel_no); + + //亮度与对比度设置 + HK_NET_API hk_net_dev_set_brightness_and_contrast(HKHANDLE h, unsigned int ui_channel_no, int i_brightness, int i_contrast); + HK_NET_API hk_net_dev_get_brightness_and_contrast(HKHANDLE h, unsigned int ui_channel_no, int *pi_brightness, int *pi_contrast); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/riis-system/src/main/resources/win32-x86-64/hk_common.dll b/riis-system/src/main/resources/win32-x86-64/hk_common.dll new file mode 100644 index 0000000..2a07dab Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hk_common.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hk_log.dll b/riis-system/src/main/resources/win32-x86-64/hk_log.dll new file mode 100644 index 0000000..ccf29e8 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hk_log.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hk_net.dll b/riis-system/src/main/resources/win32-x86-64/hk_net.dll new file mode 100644 index 0000000..ac903ec Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hk_net.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hk_net_dev_setting.dll b/riis-system/src/main/resources/win32-x86-64/hk_net_dev_setting.dll new file mode 100644 index 0000000..efb4fd2 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hk_net_dev_setting.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hk_net_video_stream.dll b/riis-system/src/main/resources/win32-x86-64/hk_net_video_stream.dll new file mode 100644 index 0000000..73d71ba Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hk_net_video_stream.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hk_ptz_ctrl.dll b/riis-system/src/main/resources/win32-x86-64/hk_ptz_ctrl.dll new file mode 100644 index 0000000..09b3a54 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hk_ptz_ctrl.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hlog.dll b/riis-system/src/main/resources/win32-x86-64/hlog.dll new file mode 100644 index 0000000..fe2298a Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hlog.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/hpr.dll b/riis-system/src/main/resources/win32-x86-64/hpr.dll new file mode 100644 index 0000000..eef0d55 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/hpr.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/libeay32.dll b/riis-system/src/main/resources/win32-x86-64/libeay32.dll new file mode 100644 index 0000000..3b73bce Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/libeay32.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/libmmd.dll b/riis-system/src/main/resources/win32-x86-64/libmmd.dll new file mode 100644 index 0000000..8becb5e Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/libmmd.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/ssleay32.dll b/riis-system/src/main/resources/win32-x86-64/ssleay32.dll new file mode 100644 index 0000000..1a45a31 Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/ssleay32.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/wpcap.dll b/riis-system/src/main/resources/win32-x86-64/wpcap.dll new file mode 100644 index 0000000..eabb73d Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/wpcap.dll differ diff --git a/riis-system/src/main/resources/win32-x86-64/zlib1.dll b/riis-system/src/main/resources/win32-x86-64/zlib1.dll new file mode 100644 index 0000000..2efbd7a Binary files /dev/null and b/riis-system/src/main/resources/win32-x86-64/zlib1.dll differ diff --git a/riis-system/src/test/java/com/yfd/platform/Test.java b/riis-system/src/test/java/com/yfd/platform/Test.java new file mode 100644 index 0000000..2f63358 --- /dev/null +++ b/riis-system/src/test/java/com/yfd/platform/Test.java @@ -0,0 +1,19 @@ +package com.yfd.platform; + +import com.yfd.platform.utils.FtpClient; +import org.springframework.boot.test.context.SpringBootTest; + +import javax.annotation.Resource; + +@SpringBootTest +public class Test { + + @Resource + private FtpClient ftpClient; + + @org.junit.jupiter.api.Test + public void test() throws Exception { + String filePath = "测试图片文件.jpg"; + ftpClient.downloadFile(filePath,"D:\\riis\\video\\"+filePath); + } +} diff --git a/riis-system/web/WEB-INF/web.xml b/riis-system/web/WEB-INF/web.xml new file mode 100644 index 0000000..d80081d --- /dev/null +++ b/riis-system/web/WEB-INF/web.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file