Parcourir la source

视频中心视频流处理

lipenghui il y a 1 mois
Parent
commit
eb15e17940
11 fichiers modifiés avec 593 ajouts et 93 suppressions
  1. 16 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/enums/Direct.java
  2. 59 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/ptz/AsyncManager.java
  3. 98 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/ptz/PtzController.java
  4. 72 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/ptz/Threads.java
  5. 86 93
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/sip/handler/req/message/response/cmdType/RecordInfoHandler.java
  6. 23 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/utils/MessageUtils.java
  7. 169 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/utils/SpringUtils.java
  8. 10 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/play/model/PtzDirectionInput.java
  9. 11 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/play/model/PtzscaleInput.java
  10. 8 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/ptz/IPtzCmdService.java
  11. 41 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/ptz/PtzCmdServiceImpl.java

+ 16 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/enums/Direct.java

@@ -10,8 +10,24 @@ public enum Direct {
     DOWN(0x04),
     LEFT(0x02),
     RIGHT(0x01),
+
+    LEFTUP(0x0A),
+    RIGHTUP(0x09),
+    LEFTDOWN(0x06),
+    RIGHTDOWN(0x05),
+
+    // 镜头放大/缩小
     ZOOM_IN(0x10),
     ZOOM_OUT(0x20),
+
+    // 光圈放大/缩小
+    IRIS_IN(0x44),
+    IRIS_OUT(0x48),
+
+    // 镜头聚焦/放焦
+    FOCUS_IN(0x41),
+    FOCUS_OUT(0x42),
+
     STOP(0) {
         @Override
         public int merge(int code) {

+ 59 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/ptz/AsyncManager.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.pms.controller.admin.yanfan.ptz;
+
+
+import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
+
+import java.util.TimerTask;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 异步任务管理器
+ *
+ * @author ruoyi
+ */
+public class AsyncManager {
+    private static final AsyncManager me = new AsyncManager();
+    /**
+     * 操作延迟10毫秒
+     */
+    private final int OPERATE_DELAY_TIME = 10;
+    /**
+     * 异步操作任务调度线程池
+     */
+    private final ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
+
+    /**
+     * 单例模式
+     */
+    private AsyncManager() {
+    }
+
+    public static AsyncManager me() {
+        return me;
+    }
+
+    /**
+     * 执行任务
+     *
+     * @param task 任务
+     */
+    public void execute(TimerTask task) {
+        executor.execute(task);
+    }
+
+    public void executeDelay(TimerTask task) {
+        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
+    }
+
+    public void executeDelay(TimerTask task, long delay) {
+        executor.schedule(task, delay, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * 停止任务线程池
+     */
+    public void shutdown() {
+        Threads.shutdownAndAwaitTermination(executor);
+    }
+}

+ 98 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/ptz/PtzController.java

@@ -0,0 +1,98 @@
+package cn.iocoder.yudao.module.pms.controller.admin.yanfan.ptz;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.pms.controller.admin.yanfan.enums.Direct;
+import cn.iocoder.yudao.module.pms.controller.admin.yanfan.utils.MessageUtils;
+import cn.iocoder.yudao.module.pms.service.yanfan.play.model.PtzDirectionInput;
+import cn.iocoder.yudao.module.pms.service.yanfan.play.model.PtzscaleInput;
+import cn.iocoder.yudao.module.pms.service.yanfan.ptz.IPtzCmdService;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.TimerTask;
+import java.util.concurrent.CompletableFuture;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Slf4j
+@RestController
+@RequestMapping("/sip/ptz")
+public class PtzController {
+    @Autowired
+    private IPtzCmdService ptzCmdService;
+
+    @Operation(summary="云台方向控制")
+    //@PreAuthorize("@ss.hasPermi('iot:video:list')")
+    @PostMapping("/direction/{deviceId}/{channelId}")
+    public CommonResult<Boolean> direction(@PathVariable String deviceId, @PathVariable String channelId, @RequestBody PtzDirectionInput ptzDirectionInput) {
+        Direct direct = null;
+        int leftRight = ptzDirectionInput.getLeftRight();
+        int upDown = ptzDirectionInput.getUpDown();
+        if (leftRight == 1) {
+            direct = Direct.RIGHT;
+        } else if (leftRight == 2) {
+            direct = Direct.LEFT;
+        } else if (upDown == 1) {
+            direct = Direct.UP;
+        } else if (upDown == 2) {
+            direct = Direct.DOWN;
+        } else {
+            direct = Direct.STOP;
+        }
+        Integer speed = ptzDirectionInput.getMoveSpeed();
+        return success(ptzCmdService.directPtzCmd(deviceId, channelId, direct, speed));
+    }
+
+    @Operation(summary="云台缩放控制")
+    //@PreAuthorize("@ss.hasPermi('iot:video:list')")
+    @PostMapping("/scale/{deviceId}/{channelId}")
+    public CommonResult<Boolean> scale(@PathVariable String deviceId, @PathVariable String channelId, @RequestBody PtzscaleInput ptzscaleInput) {
+        Direct direct = null;
+        if (ptzscaleInput.getInOut() == 1) {
+            direct = Direct.ZOOM_IN;
+        } else if (ptzscaleInput.getInOut() == 2) {
+            direct = Direct.ZOOM_OUT;
+        } else if (ptzscaleInput.getIrisInOut() == 1) {
+            direct = Direct.IRIS_IN;
+        } else if (ptzscaleInput.getIrisInOut() == 2) {
+            direct = Direct.IRIS_OUT;
+        } else if (ptzscaleInput.getFocusInOut() == 1) {
+            direct = Direct.FOCUS_IN;
+        } else if (ptzscaleInput.getFocusInOut() == 2) {
+            direct = Direct.FOCUS_OUT;
+        } else {
+            direct = Direct.STOP;
+        }
+        Integer speed = ptzscaleInput.getScaleSpeed();
+        boolean result = ptzCmdService.directPtzCmd(deviceId, channelId, direct, speed);
+        if (result) {
+//            AsyncManager.me().executeDelay(new TimerTask() {
+//                @Override
+//                public void run() {
+//                    ptzCmdService.directPtzCmd(deviceId, channelId, Direct.STOP, speed);
+//                }
+//            }, 500);
+            CompletableFuture.runAsync(()->{
+               ptzCmdService.directPtzCmd(deviceId, channelId,Direct.STOP,speed);
+            });
+        }
+       return success(result);
+    }
+
+    @Operation(summary="云台ptz控制")
+    @PostMapping("/ptz/{deviceId}/{channelId}/{direct}/{speed}")
+    //@PreAuthorize("@ss.hasPermi('iot:video:list')")
+    public CommonResult<Boolean> ptzControl(@PathVariable String deviceId, @PathVariable String channelId, @PathVariable Direct direct, @PathVariable Integer speed) {
+        return success(ptzCmdService.directPtzCmd(deviceId, channelId, direct, speed));
+    }
+
+    @Operation(summary="云台停止控制")
+    @PostMapping("/ptz/{deviceId}/{channelId}/STOP")
+    //@PreAuthorize("@ss.hasPermi('iot:video:list')")
+    public CommonResult<Boolean> ptzControlStop(@PathVariable String deviceId, @PathVariable String channelId) {
+        return success(ptzCmdService.directPtzCmd(deviceId, channelId, Direct.STOP, 0));
+    }
+
+}

+ 72 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/ptz/Threads.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.pms.controller.admin.yanfan.ptz;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.*;
+
+/**
+ * 线程相关工具类.
+ *
+ * @author ruoyi
+ */
+public class Threads {
+    private static final Logger logger = LoggerFactory.getLogger(Threads.class);
+
+    /**
+     * sleep等待,单位为毫秒
+     */
+    public static void sleep(long milliseconds) {
+        try {
+            Thread.sleep(milliseconds);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    /**
+     * 停止线程池
+     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
+     * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
+     * 如果仍然超時,則強制退出.
+     * 另对在shutdown时线程本身被调用中断做了处理.
+     */
+    public static void shutdownAndAwaitTermination(ExecutorService pool) {
+        if (pool != null && !pool.isShutdown()) {
+            pool.shutdown();
+            try {
+                if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
+                    pool.shutdownNow();
+                    if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
+                        logger.info("Pool did not terminate");
+                    }
+                }
+            } catch (InterruptedException ie) {
+                pool.shutdownNow();
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+
+    /**
+     * 打印线程异常信息
+     */
+    public static void printException(Runnable r, Throwable t) {
+        if (t == null && r instanceof Future<?>) {
+            try {
+                Future<?> future = (Future<?>) r;
+                if (future.isDone()) {
+                    future.get();
+                }
+            } catch (CancellationException ce) {
+                t = ce;
+            } catch (ExecutionException ee) {
+                t = ee.getCause();
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        if (t != null) {
+            logger.error(t.getMessage(), t);
+        }
+    }
+}

+ 86 - 93
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/sip/handler/req/message/response/cmdType/RecordInfoHandler.java

@@ -1,93 +1,86 @@
-package cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.req.message.response.cmdType;
-
-import com.yanfan.sip.domain.SipDevice;
-import com.yanfan.sip.handler.req.ReqAbstractHandler;
-import com.yanfan.sip.handler.req.message.IMessageHandler;
-import com.yanfan.sip.handler.req.message.response.ResponseMessageHandler;
-import com.yanfan.sip.model.RecordItem;
-import com.yanfan.sip.model.RecordList;
-import com.yanfan.sip.server.RecordCacheManager;
-import com.yanfan.sip.service.ISipCacheService;
-import com.yanfan.sip.util.SipUtil;
-import com.yanfan.sip.util.XmlUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.dom4j.DocumentException;
-import org.dom4j.Element;
-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 java.text.ParseException;
-import java.util.Iterator;
-
-@Slf4j
-@Component
-public class RecordInfoHandler extends ReqAbstractHandler implements InitializingBean, IMessageHandler {
-
-    @Autowired
-    private ResponseMessageHandler responseMessageHandler;
-
-    @Autowired
-    private ISipCacheService sipCacheService;
-
-    @Autowired
-    private RecordCacheManager recordCacheManager;
-    @Override
-    public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
-        try {
-            // 回复200 OK
-            responseAck(evt);
-            Element rootElement = getRootElement(evt);
-            String deviceId = rootElement.element("DeviceID").getText();
-            String sn = XmlUtil.getText(rootElement, "SN");
-            String sumNum = XmlUtil.getText(rootElement, "SumNum");
-            String recordkey = deviceId + ":" + sn;
-            int recordnum = 0;
-            RecordList recordList = recordCacheManager.get(recordkey);
-            recordList.setDeviceID(deviceId);
-            Element recordListElement = rootElement.element("RecordList");
-            if (recordListElement == null || sumNum == null || sumNum.equals("")) {
-                log.info("无录像数据");
-            } else {
-                Iterator<Element> recordListIterator = recordListElement.elementIterator();
-                if (recordListIterator != null) {
-                    while (recordListIterator.hasNext()) {
-                        Element itemRecord = recordListIterator.next();
-                        Element recordElement = itemRecord.element("DeviceID");
-                        if (recordElement == null) {
-                            continue;
-                        }
-                        RecordItem item = new RecordItem();
-                        item.setStart(SipUtil.ISO8601Totimestamp(XmlUtil.getText(itemRecord, "StartTime")));
-                        item.setEnd(SipUtil.ISO8601Totimestamp(XmlUtil.getText(itemRecord, "EndTime")));
-                        recordList.addItem(item);
-                        recordnum++;
-                    }
-                }
-            }
-            log.info("getSumNum:{}", recordList.getSumNum());
-            recordList.setSumNum(recordList.getSumNum() + recordnum);
-            if (recordList.getSumNum() == Integer.parseInt(sumNum)) {
-                //时间合并 保存到redia
-                recordList.mergeItems();
-                log.info("mergeItems recordList:{}", recordList);
-                recordCacheManager.remove(recordkey);
-                sipCacheService.setRecordList(recordkey, recordList);
-                //发布设备property到emqx
-            } else {
-                recordCacheManager.put(recordkey, recordList);
-            }
-        } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
-            e.printStackTrace();
-        }
-    }
-
-    @Override
-    public void afterPropertiesSet() throws Exception {
-        String cmdType = "RecordInfo";
-        responseMessageHandler.addHandler(cmdType, this);
-    }
-}
+//package cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.req.message.response.cmdType;
+//
+//import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.req.ReqAbstractHandler;
+//import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.req.message.IMessageHandler;
+//import cn.iocoder.yudao.module.pms.controller.admin.yanfan.sip.handler.req.message.response.ResponseMessageHandler;
+//import lombok.extern.slf4j.Slf4j;
+//import org.dom4j.DocumentException;
+//import org.dom4j.Element;
+//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 java.text.ParseException;
+//import java.util.Iterator;
+//
+//@Slf4j
+//@Component
+//public class RecordInfoHandler extends ReqAbstractHandler implements InitializingBean, IMessageHandler {
+//
+//    @Autowired
+//    private ResponseMessageHandler responseMessageHandler;
+//
+//    @Autowired
+//    private ISipCacheService sipCacheService;
+//
+//    @Autowired
+//    private RecordCacheManager recordCacheManager;
+//    @Override
+//    public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
+//        try {
+//            // 回复200 OK
+//            responseAck(evt);
+//            Element rootElement = getRootElement(evt);
+//            String deviceId = rootElement.element("DeviceID").getText();
+//            String sn = XmlUtil.getText(rootElement, "SN");
+//            String sumNum = XmlUtil.getText(rootElement, "SumNum");
+//            String recordkey = deviceId + ":" + sn;
+//            int recordnum = 0;
+//            RecordList recordList = recordCacheManager.get(recordkey);
+//            recordList.setDeviceID(deviceId);
+//            Element recordListElement = rootElement.element("RecordList");
+//            if (recordListElement == null || sumNum == null || sumNum.equals("")) {
+//                log.info("无录像数据");
+//            } else {
+//                Iterator<Element> recordListIterator = recordListElement.elementIterator();
+//                if (recordListIterator != null) {
+//                    while (recordListIterator.hasNext()) {
+//                        Element itemRecord = recordListIterator.next();
+//                        Element recordElement = itemRecord.element("DeviceID");
+//                        if (recordElement == null) {
+//                            continue;
+//                        }
+//                        RecordItem item = new RecordItem();
+//                        item.setStart(SipUtil.ISO8601Totimestamp(XmlUtil.getText(itemRecord, "StartTime")));
+//                        item.setEnd(SipUtil.ISO8601Totimestamp(XmlUtil.getText(itemRecord, "EndTime")));
+//                        recordList.addItem(item);
+//                        recordnum++;
+//                    }
+//                }
+//            }
+//            log.info("getSumNum:{}", recordList.getSumNum());
+//            recordList.setSumNum(recordList.getSumNum() + recordnum);
+//            if (recordList.getSumNum() == Integer.parseInt(sumNum)) {
+//                //时间合并 保存到redia
+//                recordList.mergeItems();
+//                log.info("mergeItems recordList:{}", recordList);
+//                recordCacheManager.remove(recordkey);
+//                sipCacheService.setRecordList(recordkey, recordList);
+//                //发布设备property到emqx
+//            } else {
+//                recordCacheManager.put(recordkey, recordList);
+//            }
+//        } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+//            e.printStackTrace();
+//        }
+//    }
+//
+//    @Override
+//    public void afterPropertiesSet() throws Exception {
+//        String cmdType = "RecordInfo";
+//        responseMessageHandler.addHandler(cmdType, this);
+//    }
+//}

+ 23 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/utils/MessageUtils.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.pms.controller.admin.yanfan.utils;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * 获取i18n资源文件
+ *
+ * @author ruoyi
+ */
+public class MessageUtils {
+    /**
+     * 根据消息键和参数 获取消息 委托给spring messageSource
+     *
+     * @param code 消息键
+     * @param args 参数
+     * @return 获取国际化翻译值
+     */
+    public static String message(String code, Object... args) {
+        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
+        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
+    }
+}

+ 169 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/yanfan/utils/SpringUtils.java

@@ -0,0 +1,169 @@
+package cn.iocoder.yudao.module.pms.controller.admin.yanfan.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 org.springframework.util.CollectionUtils;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ *
+ * @author ruoyi
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
+    /**
+     * Spring应用上下文环境
+     */
+    private static ConfigurableListableBeanFactory beanFactory;
+
+    private static ApplicationContext applicationContext;
+
+    /**
+     * 获取对象
+     *
+     * @param name
+     * @return Object 一个以所给名字注册的bean的实例
+     * @throws BeansException
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getBean(String name) throws BeansException {
+        return (T) beanFactory.getBean(name);
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws BeansException
+     */
+    public static <T> T getBean(Class<T> clz) throws BeansException {
+        T result = beanFactory.getBean(clz);
+        return result;
+    }
+
+    /**
+     * 获取类型为requiredType的对象
+     *
+     * @param clz
+     * @return
+     * @throws BeansException
+     */
+    public static <T> T getBean(String name, Class<T> clz) throws BeansException {
+        T result = beanFactory.getBean(name, 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 NoSuchBeanDefinitionException
+     */
+    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.isSingleton(name);
+    }
+
+    /**
+     * @param name
+     * @return Class 注册对象的类型
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getType(name);
+    }
+
+    /**
+     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+     *
+     * @param name
+     * @return
+     * @throws NoSuchBeanDefinitionException
+     */
+    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+        return beanFactory.getAliases(name);
+    }
+
+    /**
+     * 获取aop代理对象
+     *
+     * @param invoker
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T getAopProxy(T invoker) {
+        return (T) AopContext.currentProxy();
+    }
+
+    /**
+     * 获取当前的环境配置,无配置返回null
+     *
+     * @return 当前的环境配置
+     */
+    public static String[] getActiveProfiles() {
+        return applicationContext.getEnvironment().getActiveProfiles();
+    }
+
+
+    /**
+     * 获取配置文件中的值
+     *
+     * @param key 配置文件的key
+     * @return 当前的配置文件的值
+     */
+    public static String getRequiredProperty(String key) {
+        return applicationContext.getEnvironment().getRequiredProperty(key);
+    }
+
+    /**
+     * 获取带有annotation注解的所有bean集合
+     *
+     * @param annotation 注解
+     * @param <T>
+     * @return 集合
+     */
+    public static <T> Map<String, T> getBeanWithAnnotation(Class<? extends Annotation> annotation) {
+        Map<String, T> resultMap = new HashMap<>();
+        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(annotation);
+        if (CollectionUtils.isEmpty(beanMap)) {
+            return resultMap;
+        }
+        beanMap.forEach((key, value) -> {
+            resultMap.put(key, (T) value);
+        });
+        return resultMap;
+    }
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+        SpringUtils.beanFactory = beanFactory;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        SpringUtils.applicationContext = applicationContext;
+    }
+
+}

+ 10 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/play/model/PtzDirectionInput.java

@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.module.pms.service.yanfan.play.model;
+
+import lombok.Data;
+
+@Data
+public class PtzDirectionInput {
+    int leftRight;
+    int upDown;
+    int moveSpeed;
+}

+ 11 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/play/model/PtzscaleInput.java

@@ -0,0 +1,11 @@
+package cn.iocoder.yudao.module.pms.service.yanfan.play.model;
+
+import lombok.Data;
+
+@Data
+public class PtzscaleInput {
+    int inOut;
+    int irisInOut;
+    int focusInOut;
+    int scaleSpeed;
+}

+ 8 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/ptz/IPtzCmdService.java

@@ -0,0 +1,8 @@
+package cn.iocoder.yudao.module.pms.service.yanfan.ptz;
+
+
+import cn.iocoder.yudao.module.pms.controller.admin.yanfan.enums.Direct;
+
+public interface IPtzCmdService {
+    public boolean directPtzCmd(String deviceId, String channelId, Direct direct, Integer speed);
+}

+ 41 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/yanfan/ptz/PtzCmdServiceImpl.java

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.pms.service.yanfan.ptz;
+
+import cn.iocoder.yudao.module.pms.controller.admin.yanfan.enums.Direct;
+import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.sip.device.YfSipDeviceDO;
+import cn.iocoder.yudao.module.pms.service.yanfan.sip.MessageInvoker;
+import cn.iocoder.yudao.module.pms.service.yanfan.sip.device.YfSipDeviceService;
+import cn.iocoder.yudao.module.pms.service.yanfan.sip.msg.DeviceControl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+@Slf4j
+@Service
+public class PtzCmdServiceImpl implements IPtzCmdService {
+
+    @Autowired
+    private MessageInvoker messageInvoker;
+
+    @Autowired
+    private YfSipDeviceService sipDeviceService;
+
+
+    public boolean directPtzCmd(String deviceId, String channelId, Direct direct, Integer speed) {
+        Map<Direct, Integer> directAndSpeed = new HashMap<>();
+        directAndSpeed.put(direct, speed);
+        YfSipDeviceDO dev = sipDeviceService.selectSipDeviceBySipId(deviceId);
+        if (dev != null) {
+            DeviceControl control = new DeviceControl();
+            control.setPtzDirect(directAndSpeed);
+            control.setDeviceId(channelId);
+            messageInvoker.deviceControl(dev, control);
+            return true;
+        }
+        return false;
+    }
+
+}