Sfoglia il codice sorgente

Merge branch 'master' of https://gogs.tjp.com.cn/maoming/python-fastapi-mm-zhcs-yj-api

libushang 3 mesi fa
parent
commit
7a9840e65b

+ 156 - 0
jobs/hkvideo_job.py

@@ -2,6 +2,7 @@
 # -*- coding: utf-8 -*-
 from datetime import datetime
 from sqlalchemy.orm import Session
+from sqlalchemy import text
 from utils import *
 from utils.redis_util import *
 from models import *
@@ -20,6 +21,7 @@ def proc():
         db = get_db_local()
 
         refresh_hkvideo(db)
+        refresh_hkvideo_list(db)
 
         db.close()
 
@@ -48,3 +50,157 @@ def refresh_hkvideo(db: Session):
             
 def update_assis_video_table(video_code: str, status: int, db: Session) -> None:
     pass
+
+
+# def refresh_hkvideo_list(db: Session):
+#     api = hk_video_api.HikvisionAPI()
+#     data = api.get_cameras()
+#     if isinstance(data,dict):
+#         if data['code']=='0':
+#             total = data['data']['total']
+#             pageSize= 1000
+#             totalPages = (total + pageSize - 1) // pageSize
+#             infos = []
+#             for i in range(totalPages):
+#                 data = api.get_cameras(i+1,pageSize)
+#                 if data['code'] == '0':
+#                     list = data['data']['list']
+#                     for i in list:
+#                         infos.append(TPVideoInfo(cameraIndexCode = i['cameraIndexCode'],
+#                                                 gbIndexCode = i['gbIndexCode'],
+#                                                 name = i['name'],
+#                                                 longitude = i['longitude'],
+#                                                 latitude = i['latitude'],
+#                                                 altitude = i['altitude'],
+#                                                 pixel = i['pixel'],
+#                                                 cameraType = i['cameraType'],
+#                                                 cameraTypeName = i['cameraTypeName'],
+#                                                 installPlace = i['installPlace'],
+#                                                 updateTime = i['updateTime'],
+#                                                 createTime = i['createTime'],
+#                                                 status =i['status'],
+#                                                 statusName = i['statusName']))
+#             db.execute(text(f"TRUNCATE TABLE tp_video_info;"))
+#             db.add_all(infos)
+#             db.commit()
+
+
+def refresh_hkvideo_list(db: Session):
+    api = hk_video_api.HikvisionAPI()
+    data = api.get_cameras()
+
+    if isinstance(data, dict):
+        if data['code'] == '0':
+            total = data['data']['total']
+            page_size = 1000
+            total_pages = (total + page_size - 1) // page_size
+            infos = []
+
+            for page in range(total_pages):
+                print(f'视频条数{total_pages},正在获取视频信息第{page}页')
+                data = api.get_cameras(page + 1, page_size)
+                if data['code'] == '0':
+                    camera_list = data['data']['list']
+                    for camera_info in camera_list:
+                        longitude= camera_info['longitude']
+                        latitude = camera_info['latitude']
+                        if longitude=='':
+                            longitude = 0.0
+                        if latitude=='':
+                            latitude = 0.0
+                        info = TPVideoInfo(
+                            cameraIndexCode=camera_info['cameraIndexCode'],
+                            gbIndexCode=camera_info['gbIndexCode'],
+                            name=camera_info['name'],
+                            longitude=longitude,
+                            latitude=latitude,
+                            altitude=camera_info['altitude'],
+                            pixel=camera_info['pixel'],
+                            cameraType=camera_info['cameraType'],
+                            unitIndexCode = camera_info['unitIndexCode'],
+                            cameraTypeName=camera_info['cameraTypeName'],
+                            installPlace=camera_info['installPlace'],
+                            updateTime=camera_info['updateTime'],
+                            createTime=camera_info['createTime'],
+                            status=camera_info['status'],
+                            statusName=camera_info['statusName']
+                        )
+                        infos.append(info)
+                else:
+                    print(f"Failed to fetch cameras on page {page + 1}")
+                    return
+
+            try:
+                # 清空表
+                db.execute(text("TRUNCATE TABLE tp_video_info;"))
+                # 插入新数据
+                db.add_all(infos)
+                db.commit()
+            except Exception as e:
+                db.rollback()  # 回滚事务
+                print(f"Error occurred during database operations: {e}")
+        else:
+            print("Failed to fetch initial camera data")
+    else:
+        print("API response is not in the expected format")
+
+
+def refresh_hkvideo_region_list(db: Session):
+    api = hk_video_api.HikvisionAPI()
+    data = api.get_regions()
+
+    if isinstance(data, dict):
+        if data['code'] == '0':
+            total = data['data']['total']
+            page_size = 1000
+            total_pages = (total + page_size - 1) // page_size
+            infos = []
+
+            for page in range(total_pages):
+                print(f'视频条数{total_pages},正在获取视频信息第{page}页')
+                data = api.get_regions(page + 1, page_size)
+                if data['code'] == '0':
+                    region_list = data['data']['list']
+                    for cregion_info in region_list:
+                        pass
+                        # longitude= camera_info['longitude']
+                        # latitude = camera_info['latitude']
+                        # if longitude=='':
+                        #     longitude = 0.0
+                        # if latitude=='':
+                        #     latitude = 0.0
+                        # info = TPVideoInfo(
+                        #     cameraIndexCode=camera_info['cameraIndexCode'],
+                        #     gbIndexCode=camera_info['gbIndexCode'],
+                        #     name=camera_info['name'],
+                        #     longitude=longitude,
+                        #     latitude=latitude,
+                        #     altitude=camera_info['altitude'],
+                        #     pixel=camera_info['pixel'],
+                        #     cameraType=camera_info['cameraType'],
+                        #     unitIndexCode = camera_info['unitIndexCode'],
+                        #     cameraTypeName=camera_info['cameraTypeName'],
+                        #     installPlace=camera_info['installPlace'],
+                        #     updateTime=camera_info['updateTime'],
+                        #     createTime=camera_info['createTime'],
+                        #     status=camera_info['status'],
+                        #     statusName=camera_info['statusName']
+                        # )
+                        # infos.append(info)
+                else:
+                    print(f"Failed to fetch cameras on page {page + 1}")
+                    return
+
+            try:
+                # 清空表
+                db.execute(text("TRUNCATE TABLE tp_video_region;"))
+                # 插入新数据
+                db.add_all(infos)
+                db.commit()
+            except Exception as e:
+                db.rollback()  # 回滚事务
+                print(f"Error occurred during database operations: {e}")
+        else:
+            print("Failed to fetch initial camera data")
+    else:
+        print("API response is not in the expected format")

+ 37 - 1
models/video_base.py

@@ -167,4 +167,40 @@ class VideoJob(Base):
     update_time = Column(DateTime, default=datetime.now, nullable=True, comment='更新时间')
 
     class Config:
-        orm_mode = True
+        orm_mode = True
+
+class TPVideoInfo(Base):
+    __tablename__ = "tp_video_info"
+
+    cameraIndexCode = Column(String(100), primary_key=True, comment="摄像头索引码")
+    gbIndexCode = Column(String(100), comment="国标索引码")
+    name = Column(String(255), comment="摄像头名称")
+    longitude = Column(Float, comment="经度")
+    latitude = Column(Float, comment="纬度")
+    altitude = Column(String(255), comment="海拔")
+    pixel = Column(String(50), comment="像素")
+    cameraType = Column(String(50), comment="摄像头类型代码")
+    cameraTypeName = Column(String(100), comment="摄像头类型名称")
+    installPlace = Column(String(255), comment="安装位置")
+    unitIndexCode =  Column(String(255), comment="所属组织编号")
+    updateTime = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="更新时间")
+    createTime = Column(DateTime, server_default=func.now(), comment="创建时间")
+    status = Column(String(255), comment="状态码")
+    statusName = Column(String(250), comment="状态名称")
+    location = Column(String(255)) # 存储 POINT 类型的字符串表示
+
+class TPVideoRegion(Base):
+    __tablename__ = 'tp_video_region'  # 表名
+
+    # 字段定义
+    indexCode = Column(String(255),primary_key=True, nullable=False, comment='组织编号(长度1-255位)')
+    name = Column(String(255), nullable=False, comment='组织名称(长度1-255位)')
+    parentIndexCode = Column(String(255), comment='父组织编号(长度1-255位)')
+    treeCode = Column(String(255), comment='树编码')
+    externalIndexCode = Column(String(255), comment='国标编码')
+    sort = Column(Integer, comment='排序')
+    updateTime = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="更新时间")
+    createTime = Column(DateTime, server_default=func.now(), comment="创建时间")
+    status = Column(String(255), comment="状态码")
+    available=Column(Integer, comment='是否显示')
+    leaf=Column(Integer, comment='是否叶')

+ 3 - 1
routers/api/videoResource/__init__.py

@@ -6,6 +6,7 @@ from .videoinfo import router as videoinfo_router
 from .avcon import router as avcon_router
 from .wrjvideo import router as wrjvideo_router
 from .tag import router as tag_router
+from .location import router as location_router
 
 router = APIRouter()
 
@@ -13,4 +14,5 @@ router.include_router(hkvideo_router, prefix="/hkvideo")
 router.include_router(videoinfo_router, prefix="/videoinfo")
 router.include_router(tag_router, prefix="/tag")
 router.include_router(avcon_router, prefix="/avcon")
-router.include_router(wrjvideo_router, prefix="/wrjvideo")
+router.include_router(wrjvideo_router, prefix="/wrjvideo")
+router.include_router(location_router, prefix="/location")

+ 162 - 0
routers/api/videoResource/location/__init__.py

@@ -0,0 +1,162 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from fastapi import APIRouter, Request, Depends, Query, HTTPException, status
+from common.security import valid_access_token
+from fastapi.responses import JSONResponse
+from sqlalchemy.orm import Session
+from sqlalchemy import and_, or_
+from sqlalchemy.sql import func
+from sqlalchemy.future import select
+from common.auth_user import *
+from pydantic import BaseModel
+from database import get_db
+from typing import List
+from models import *
+from utils import *
+from utils.ry_system_util import *
+from utils.video_util import *
+from collections import defaultdict
+import traceback
+
+import json
+import time
+import math
+
+router = APIRouter()
+
+
+@router.get("/videos")
+async def get_videos(
+        zoom_level: float = Query(..., description="Zoom level for clustering"),
+        latitude_min: float = Query(..., description="Minimum latitude"),
+        latitude_max: float = Query(..., description="Maximum latitude"),
+        longitude_min: float = Query(..., description="Minimum longitude"),
+        longitude_max: float = Query(..., description="Maximum longitude"),
+        dict_value: str = Query(None),
+        db:  Session = Depends(get_db)
+):
+    try:
+        # 根据缩放级别动态调整分组粒度
+        distance_threshold = 1000 / (2 ** zoom_level)  # 例如:每缩放一级,距离阈值减半
+        que = True
+        print(time.time())
+        if dict_value:
+            tag_info = get_dict_data_info(db, 'video_type', dict_value)
+            if tag_info:
+                if tag_info.dict_label!='全量视频':
+                    videolist = [i.video_code for i in tag_get_video_tag_list(db,dict_value)]
+                    que =TPVideoInfo.gbIndexCode.in_(videolist)
+        # 查询分组
+        print("1",time.time())
+        query = (
+            select(
+                TPVideoInfo.cameraIndexCode,
+                TPVideoInfo.gbIndexCode,
+                TPVideoInfo.pixel,
+                TPVideoInfo.cameraType,
+                TPVideoInfo.cameraTypeName,
+                TPVideoInfo.installPlace,
+                TPVideoInfo.status,
+                TPVideoInfo.statusName,
+                TPVideoInfo.latitude,
+                TPVideoInfo.longitude,
+                TPVideoInfo.name,
+                TPVideoInfo.unitIndexCode,
+                func.ST_AsText(TPVideoInfo.location).label("location")
+            )
+            .select_from(TPVideoInfo).where(
+                and_(
+                    TPVideoInfo.latitude >= latitude_min,
+                    TPVideoInfo.latitude <= latitude_max,
+                    TPVideoInfo.longitude >= longitude_min,
+                    TPVideoInfo.longitude <= longitude_max,
+                    TPVideoInfo.longitude>0,
+                    TPVideoInfo.latitude>0,que
+                )
+            )
+            .order_by(TPVideoInfo.cameraIndexCode)
+        )
+
+        result = db.execute(query)
+        print("2",time.time())
+        videos = result.fetchall()
+        print("3",time.time())
+
+        # 动态分组逻辑
+        # groups = {}
+        groups = group_videos(videos, distance_threshold)
+        # for video in videos:
+        #     grouped = False
+        #     for group_id, group in list(groups.items()):
+        #         for v in group["videos"]:
+        #             distance = calculate_distance(video, v)
+        #             if distance < distance_threshold:
+        #                 groups[group_id]["videos"].append(video)
+        #                 groups[group_id]["count"] += 1
+        #                 grouped = True
+        #                 break
+        #         if grouped:
+        #             break
+        #     if not grouped:
+        #         group_id = video.cameraIndexCode
+        #         groups[group_id] = {"count": 1, "videos": [video]}
+
+        print("4",time.time())
+
+        return {"code": 200,
+                "msg": "操作成功",
+                "data": groups}
+    except Exception as e:
+        # 处理异常
+        traceback.print_exc()
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+def calculate_grid_size(distance_threshold):
+    # 假设地球半径为6371公里,将距离阈值转换为经纬度的差值
+    # 这里假设纬度变化对距离的影响较小,仅根据经度计算网格大小
+    earth_radius = 6371  # 地球半径,单位为公里
+    grid_size = distance_threshold / earth_radius
+    return grid_size
+
+def get_grid_key(latitude, longitude, grid_size):
+    # 根据经纬度和网格大小计算网格键
+    return (math.floor(latitude / grid_size), math.floor(longitude / grid_size))
+
+def calculate_distance(video1, video2):
+    # 使用 Haversine 公式计算两点之间的距离
+    from math import radians, sin, cos, sqrt, atan2
+    R = 6371  # 地球半径(公里)
+    lat1, lon1 = radians(video1.latitude), radians(video1.longitude)
+    lat2, lon2 = radians(video2.latitude), radians(video2.longitude)
+    dlat = lat2 - lat1
+    dlon = lon2 - lon1
+    a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
+    c = 2 * atan2(sqrt(a), sqrt(1 - a))
+    return R * c
+
+def group_videos(videos, distance_threshold):
+    grid_size = calculate_grid_size(distance_threshold)
+    grid = defaultdict(list)
+    groups = {}
+
+    for video in videos:
+        grid_key = get_grid_key(video.latitude, video.longitude, grid_size)
+        grid[grid_key].append(video)
+
+    for grid_key, grid_videos in grid.items():
+        for video in grid_videos:
+            grouped = False
+            for group_id, group in list(groups.items()):
+                for v in group["videos"]:
+                    if calculate_distance(video, v) < distance_threshold:
+                        groups[group_id]["videos"].append(video)
+                        groups[group_id]["count"] += 1
+                        grouped = True
+                        break
+                if grouped:
+                    break
+            if not grouped:
+                group_id = video.cameraIndexCode
+                groups[group_id] = {"count": 1, "videos": [video]}
+
+    return groups

+ 81 - 3
routers/api/videoResource/tag/__init__.py

@@ -20,11 +20,56 @@ import json
 
 router = APIRouter()
 
+@router.get('/notNull/list')
+async def get_dict_data_by_type(
+    # dictType: str = Query(None, max_length=100),
+    db: Session = Depends(get_db),
+    body = Depends(remove_xss_json),
+    user_id = Depends(valid_access_token)
+):
+    try:
+        # 根据 dict_type 查询字典数据
+        query = db.query(SysDictData)
+        query = query.filter(SysDictData.dict_type == 'video_type')
+        query = query.filter(SysDictData.del_flag != '2')
+        query = query.order_by(SysDictData.dict_sort)
+        # dict_data = db.query(SysDictData).filter_by(dict_type==dict_type and del_flag != '2').all()
+        dict_data = query.all()
+        # 将模型转换为字典
+        dict_data_list = []
+        for d in dict_data:
+            li = tag_get_video_tag_list(db,d.dict_value)
+            if len(li)>0:
+                dict_data_list.append({
+                    "dictCode": d.dict_code,
+                    "dictSort": d.dict_sort,
+                    "dictLabel": d.dict_label,
+                    "dictValue": d.dict_value,
+                    "dictType": d.dict_type,
+                    "cssClass": d.css_class,
+                    "listClass": d.list_class,
+                    "isDefault": d.is_default,
+                    "remark": d.remark,
+                    "createTime": d.create_time.strftime('%Y-%m-%d %H:%M:%S') if d.create_time else '',
+                    "videoNum":len(li)
+                })
+        # 构建返回结果
+        result = {
+            "code": 200,
+            "msg": "操作成功",
+            "data": dict_data_list
+        }
+        return result
+    except Exception as e:
+        # 处理异常
+
+        traceback.print_exc()
+        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
 
 @router.get('/list')
 async def get_dict_data_by_type(
     # dictType: str = Query(None, max_length=100),
-    pageNum: int = Query(1, gt=0),
+    page: int = Query(1, gt=0),
     pageSize: int = Query(10, gt=0),
     db: Session = Depends(get_db),
     body = Depends(remove_xss_json),
@@ -37,7 +82,7 @@ async def get_dict_data_by_type(
         query = query.filter(SysDictData.del_flag != '2')
         query = query.order_by(SysDictData.dict_sort)
         # dict_data = db.query(SysDictData).filter_by(dict_type==dict_type and del_flag != '2').all()
-        dict_data = query.all()
+        dict_data = query.offset((page - 1) * pageSize).limit(pageSize).all()
         # 将模型转换为字典
         dict_data_list = [
             {
@@ -125,7 +170,7 @@ async def create_dict_data(
 ):
     try:
         # 创建一个新的 SysDictData 实例
-        dict_label = body['dict_label']
+        dict_label = body['dictLabel']
         dict_info = dict_label_get_dict_data_info(db, 'video_type', dict_label)
         if dict_info is None:
             new_dict_data = SysDictData(
@@ -304,3 +349,36 @@ async def delete_dict_data(
 #         # 处理异常
 #         traceback.print_exc()
 #         raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
+
+
+
+@router.post("/labeling_video_tag")
+async def add_video_tag(
+    user_id=Depends(valid_access_token),
+    body = Depends(remove_xss_json),
+    db: Session = Depends(get_db)
+):
+    try:
+
+        old_video_tag_list = get_video_tag_list(db,body['video_code'])
+        addlist = []
+        for tag in body['dict_value_list']:
+            tag_info = get_dict_data_info(db, 'video_type', tag)
+            if tag_info is None:
+                return JSONResponse(status_code=404, content={"code": 404, "msg": f"{tag}标签不存在"})
+            new_video_tag = TpVideoTag(
+                id = new_guid(),
+                video_code=body['video_code'],
+                dict_value=tag,
+                dict_type='video_type',
+                create_by = user_id
+            )
+            addlist.append(new_video_tag)
+        db.add_all(addlist)
+        for info in old_video_tag_list:
+            info.del_flag='2'
+        db.commit()
+        return {"code": 200, "msg": "成功", "data": None}
+    except Exception as e:
+        traceback.print_exc()
+        raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")

+ 66 - 2
utils/hk_video_api.py

@@ -2,7 +2,14 @@
 # -*- coding: utf-8 -*-
 import requests
 import json
-    
+import time
+import uuid
+import hmac
+import hashlib
+import base64
+from requests.packages.urllib3.exceptions import InsecureRequestWarning
+requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
+
 def get_video_url(id: str, protocol: str):
     video_url = ""
     # api_url = "http://10.181.7.217:8081/previewURLs"
@@ -52,4 +59,61 @@ def controlling(id: str, action: int, command: str, speed: int, presetIndex: str
         result = response.json()
         if result['errcode'] == 0:
             data = result['data']
-    return data
+    return data
+
+
+class HikvisionAPI:
+    def __init__(self):
+        self.app_key = "20034027"
+        self.app_secret = "tfa3PNeYeN2MOrbRj3wS"
+        self.base_url = f"https://19.155.243.66/artemis"
+        # "/artemis/api/resource/v1/cameras"
+    def _generate_signature(self, path):
+        # 设置请求头
+        timestamp = str(int(time.time() * 1000))
+        nonce = str(uuid.uuid4())
+        headers = {
+            "Accept": "*/*",
+            "Content-Type": "application/json",
+            "x-ca-key": self.app_key,
+            "x-ca-nonce": nonce,
+            "x-ca-timestamp": timestamp,
+            "x-ca-signature-headers": "x-ca-key,x-ca-nonce,x-ca-timestamp"
+        }
+        # 拼接签名字符串
+        nonce = headers.get("x-ca-nonce")
+        timestamp = headers.get("x-ca-timestamp")
+        sign_str = "POST\n*/*\napplication/json" + "\nx-ca-key:" + self.app_key + "\nx-ca-nonce:" + \
+           nonce + "\nx-ca-timestamp:" + timestamp + "\n" + \
+           "/artemis"+path
+
+        # 生成签名
+        signature = hmac.new(self.app_secret.encode(), sign_str.encode(), hashlib.sha256).digest()
+        headers["x-ca-signature"]=base64.b64encode(signature).decode()
+        return headers
+
+    def get_cameras(self,pageNo=1,pageSize=1):
+        path = "/api/resource/v1/cameras"
+        url = f"{self.base_url}{path}"
+        body =  {
+            "pageNo": pageNo,"pageSize":pageSize
+        }
+        headers =self._generate_signature(path)
+        response = requests.post(url, headers=headers, json=body, verify=False)
+        if response.status_code == 200:
+            return response.json()
+        else:
+            raise Exception(f"Failed to get preview URL: {response.text}")
+    def get_regions(self,pageNo=1,pageSize=1):
+        path = "/api/resource/v1/regions"
+        url = f"{self.base_url}{path}"
+        body =  {
+            "pageNo": pageNo,"pageSize":pageSize,
+            "treeCode": "0"
+        }
+        headers =self._generate_signature(path)
+        response = requests.post(url, headers=headers, json=body, verify=False)
+        if response.status_code == 200:
+            return response.json()
+        else:
+            raise Exception(f"Failed to get preview URL: {response.text}")