From 1806eb80bb57e4ee26b464dda95b86ee15bc3c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E5=89=8D=E6=98=8E?= <909355014@qq.com> Date: Fri, 12 Aug 2022 17:17:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=9C=E4=B8=9A=E8=BF=87=E7=A8=8B=E4=B8=AD?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=89=A7=E8=A1=8C=E8=A7=86=E9=A2=91=E7=9B=91?= =?UTF-8?q?=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/develop/views.py | 5 + .../ecm/migrations/0014_auto_20220811_1403.py | 25 ++++ .../ecm/migrations/0015_auto_20220812_1715.py | 24 ++++ apps/ecm/models.py | 3 + apps/ecm/serializers.py | 1 + apps/ecm/service.py | 105 ++++++++++++---- apps/ecm/tasks.py | 62 +++++++++- apps/ecm/views.py | 2 +- apps/hrm/filters.py | 2 +- apps/hrm/models.py | 2 +- apps/hrm/serializers.py | 2 +- apps/hrm/services.py | 10 +- apps/hrm/views.py | 1 + .../opm/migrations/0003_auto_20220812_1715.py | 29 +++++ apps/opm/models.py | 4 + apps/opm/serializers.py | 1 + apps/opm/services.py | 26 +++- apps/opm/tasks.py | 41 ++++--- apps/opm/views.py | 1 + apps/third/dahua.py | 113 +++++++++++++++++- apps/third/tapis.py | 4 + apps/third/views.py | 6 +- apps/third/views_d.py | 16 ++- apps/utils/tools.py | 17 +++ apps/vm/serializers.py | 14 ++- 25 files changed, 451 insertions(+), 65 deletions(-) create mode 100644 apps/ecm/migrations/0014_auto_20220811_1403.py create mode 100644 apps/ecm/migrations/0015_auto_20220812_1715.py create mode 100644 apps/opm/migrations/0003_auto_20220812_1715.py diff --git a/apps/develop/views.py b/apps/develop/views.py index 12a66f87..5354448e 100755 --- a/apps/develop/views.py +++ b/apps/develop/views.py @@ -143,6 +143,11 @@ class TestViewSet(CustomGenericViewSet): ret = {} task = show.delay(*vdata.get('args', []), **vdata.get('kwargs', {})) ret['task_id'] = task.task_id + # from celery.app.control import Control + # from server.celery import app + # celery_control = Control(app=app) + # # 关闭作业监控任务 + # res = celery_control.revoke(task.task_id, terminate=True) return Response(ret) @action(methods=['post'], detail=False, serializer_class=Serializer) diff --git a/apps/ecm/migrations/0014_auto_20220811_1403.py b/apps/ecm/migrations/0014_auto_20220811_1403.py new file mode 100644 index 00000000..1d979ce9 --- /dev/null +++ b/apps/ecm/migrations/0014_auto_20220811_1403.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.12 on 2022-08-11 06:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('opm', '0002_initial'), + ('ecm', '0013_auto_20220810_1512'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='operation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='opm.operation', verbose_name='关联作业'), + ), + migrations.AddField( + model_name='eventcate', + name='opl_cates', + field=models.ManyToManyField(to='opm.OplCate', verbose_name='关联许可证'), + ), + ] diff --git a/apps/ecm/migrations/0015_auto_20220812_1715.py b/apps/ecm/migrations/0015_auto_20220812_1715.py new file mode 100644 index 00000000..6339384f --- /dev/null +++ b/apps/ecm/migrations/0015_auto_20220812_1715.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.12 on 2022-08-12 09:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('opm', '0002_initial'), + ('ecm', '0014_auto_20220811_1403'), + ] + + operations = [ + migrations.RemoveField( + model_name='event', + name='operation', + ), + migrations.AddField( + model_name='event', + name='opl', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='opm.opl', verbose_name='关联许可证'), + ), + ] diff --git a/apps/ecm/models.py b/apps/ecm/models.py index de6a7dac..b1fc1f8a 100644 --- a/apps/ecm/models.py +++ b/apps/ecm/models.py @@ -1,6 +1,7 @@ from django.db import models from apps.am.models import Area from apps.hrm.models import Employee +from apps.opm.models import Opl, OplCate from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel from apps.system.models import Post, User from apps.third.models import TDevice @@ -28,6 +29,7 @@ class EventCate(CommonAModel): default=Area.AREA_1) hanle_minute = models.PositiveSmallIntegerField('处理时间', default=0, help_text='超过处理时间事件状态变为超时未处理,0代表未配置') same_allow_minute = models.PositiveSmallIntegerField('允许时间', default=0, help_text='同一对象同事件多长时间内不再触发, 0代表一直触发') + opl_cates = models.ManyToManyField(OplCate, verbose_name='关联许可证') def __str__(self): return self.name @@ -83,6 +85,7 @@ class Event(CommonBModel): area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE, null=True, blank=True) vchannel = models.ForeignKey(TDevice, verbose_name='抓拍设备', on_delete=models.CASCADE, null=True, blank=True) location = models.JSONField('事件点位坐标', default=dict, null=False, blank=True) + opl = models.ForeignKey(Opl, verbose_name='关联许可证', on_delete=models.CASCADE, null=True, blank=True) obj_cate = models.CharField('发生对象', max_length=20, help_text='people(人员)/...') employee = models.ForeignKey(Employee, verbose_name='当事人', on_delete=models.CASCADE, null=True, blank=True) diff --git a/apps/ecm/serializers.py b/apps/ecm/serializers.py index 93880f98..f7e1c378 100644 --- a/apps/ecm/serializers.py +++ b/apps/ecm/serializers.py @@ -78,6 +78,7 @@ class EventSerializer(serializers.ModelSerializer): area_ = AreaSerializer(source='area', read_only=True) cates_ = EventCateSimpleSerializer(source='cates', read_only=True, many=True) employee_ = EmployeeSerializer(source='employee', read_only=True) + vchannel_ = TDeviceSimpleSerializer(source='vchannel', read_only=True) handle_user_name = serializers.CharField(source='handle_user.name', read_only=True) face_img = MyFilePathField() global_img = MyFilePathField() diff --git a/apps/ecm/service.py b/apps/ecm/service.py index 3e8ff822..f97444b4 100644 --- a/apps/ecm/service.py +++ b/apps/ecm/service.py @@ -77,7 +77,7 @@ def algo_handle(codes: list, data: dict): return ['helmet'] -def notify_event(event: Event): +def notify_event(event: Event, voice_msg=''): """事件后续处理: Args: @@ -87,7 +87,7 @@ def notify_event(event: Event): ep = event.employee obj_cate = event.obj_cate params = {'area': event.area.name, 'employee': '', 'event': ''} - if obj_cate == 'people' and ep: + if ep: ep_name = ep.name ep_type = '员工' if ep.type == 'rempoyee': @@ -97,11 +97,14 @@ def notify_event(event: Event): params['employee'] = ep_type + ep_name elif obj_cate == 'people': params['employee'] = '未知人员' - elif obj_cate == 'area': - params['employee'] = '区域' for i in event.cates.all(): params['event'] = params['event'] + i.name + ',' - event.voice_msg = '位于{}的{},{}请及时处理'.format(params['area'], params['employee'], params['event']) + if params['employee']: + event.voice_msg = '位于{}的{},{}请及时处理'.format(params['area'], params['employee'], params['event']) + else: + event.voice_msg = '在{}下,发生{}请及时处理'.format(params['area'], params['event']) + if voice_msg: + event.voice_msg = voice_msg event.save() # 喇叭播放 Thread(target=save_voice_and_speak, args=(event,), daemon=True).start() @@ -119,18 +122,19 @@ def save_voice_and_speak(event: Event): v_p, v_num = main_cate.voice_person, main_cate.voice_num _, event.voice, _ = generate_voice(event.voice_msg, v_p) event.save() - if event.area: # 如果事件存在发生区域 - sps = list(TDevice.objects.filter(area=event.area, type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True)) - if len(sps) == 0: # 如果当前区域没有喇叭就找覆盖区的喇叭 - sps = list(TDevice.objects.filter(areas=event.area, - type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True)) - # 查找固定喇叭 - for m in event.cates.all(): - for n in m.speakers.all(): - if n.code not in sps: - sps.append(n.code) - if sps: - spClient.speak(event.voice, sps, v_num) + if main_cate.speaker_on: + if event.area: # 如果事件存在发生区域 + sps = list(TDevice.objects.filter(area=event.area, type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True)) + if len(sps) == 0: # 如果当前区域没有喇叭就找覆盖区的喇叭 + sps = list(TDevice.objects.filter(areas=event.area, + type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True)) + # 查找固定喇叭 + for m in event.cates.all(): + for n in m.speakers.all(): + if n.code not in sps: + sps.append(n.code) + if sps: + spClient.speak(event.voice, sps, v_num) def create_remind(event: Event, params: dict): @@ -221,10 +225,10 @@ def dispatch_dahua_event(data: dict): event = None if alarm_type in [1001003, 1001000] and vchannel: # 内部人员/或陌生人报警 # 查看加载的算法 - algo_codes = list(AlgoChannel.objects.filter(vchannel=vchannel).exclude( + algo_codes = list(AlgoChannel.objects.filter(vchannel=vchannel, algo__self_algo=True).exclude( algo__code=None).order_by('algo__priority', 'algo__create_time').values_list('algo__code', flat=True)) area = vchannel.area # 视频所在区域 - if algo_codes and area: # 如果加载了算法且视频通道绑定区域才继续 + if algo_codes: # 如果加载了算法才继续 face_img_o = dhClient.get_full_pic(data['info']['alarmPicture']) global_img_o = dhClient.get_full_pic(data['info']['extend']['globalScenePicUrl']) obj_cate = 'people' @@ -250,6 +254,59 @@ def dispatch_dahua_event(data: dict): 'cate': i, 'event': event }) + elif alarm_type in [302, 303] and vchannel: + ec = EventCate.objects.filter(code=str(alarm_type)).first() + event = None + global_img_o = dhClient.get_full_pic(data['info']['alarmPicture']) + happen_time = timestamp_to_time(int(data['info']['alarmDate'])) + if ec: # 先查看是否有定义该事件种类 + event = Event() + event.global_img = save_dahua_pic(global_img_o) + event.area = vchannel.area + event.obj_cate = 'other' + event.vchannel = vchannel + event.happen_time = happen_time + event.save() + Eventdo.objects.get_or_create(cate=ec, event=event, defaults={ + 'cate': ec, + 'event': event + }) + # 查看加载的算法 + algo_codes = list(AlgoChannel.objects.filter(vchannel=vchannel, algo__self_algo=True).exclude( + algo__code=None).order_by('algo__priority', 'algo__create_time').values_list('algo__code', flat=True)) + if algo_codes: + ec_codes = algo_handle(algo_codes, data={}) # 算法处理 + if ec_codes: + # 获取本次所有发生事件种类 + ecs = EventCate.objects.filter(code__in=ec_codes) + obj_cate = 'other' + ep = None + if 'helmet' in ec_codes: + # 如果存在安全帽事件 + # 尝试以图搜图找到当事人 + res = dhClient.face_search(path=global_img_o) + if res and res[0]: + ep = Employee.objects.filter(id_number=res[0]['identity']).first() + if ep: + obj_cate = 'people' + if event is None: # 如果未创建事件 + event = Event() + event.global_img = save_dahua_pic(global_img_o) + event.area = vchannel.area + event.obj_cate = obj_cate + event.employee = ep + event.vchannel = vchannel + event.happen_time = happen_time + event.save() + else: + event.obj_cate = obj_cate + event.employee = ep + event.save() + for i in ecs: + Eventdo.objects.get_or_create(cate=i, event=event, defaults={ + 'cate': i, + 'event': event + }) if event: notify_event(event) @@ -450,7 +507,7 @@ def blt_online(data): blts = TDevice.objects.filter(code=data['userId']).first() if blts.employee: ep = blts.employee - if ep.type == 'employee' and ep.is_at_work is False: + if ep.type == 'employee' and ep.is_atwork is False: # 上班打卡 now = timezone.now() cr_10 = ClockRecord.objects.filter(type=10, employee=ep, create_time__year=now.year, @@ -468,7 +525,8 @@ def blt_online(data): cr_10.trigger = 'location' cr_10.detail = data cr_10.save() - ep.is_at_work = True + ep.is_atwork = True + ep.last_check_time = now ep.save() @@ -477,7 +535,7 @@ def blt_offline(data): # 定位离线 if blts.employee: ep = blts.employee - if ep.type == 'employee' and ep.is_at_work: + if ep.type == 'employee' and ep.is_atwork: # 下班打卡 now = timezone.now() cr_20 = ClockRecord.objects.filter(type=20, employee=ep, create_time__year=now.year, @@ -495,7 +553,8 @@ def blt_offline(data): cr_20.trigger = 'location' cr_20.detail = data cr_20.save() - ep.is_at_work = False + ep.is_atwork = False + ep.last_check_time = now ep.save() diff --git a/apps/ecm/tasks.py b/apps/ecm/tasks.py index d2f4ebc0..d89e4c2f 100644 --- a/apps/ecm/tasks.py +++ b/apps/ecm/tasks.py @@ -6,11 +6,16 @@ from celery import shared_task from apps.am.models import Area from apps.ecm.models import EventCate, Eventdo -from apps.ecm.service import notify_event +from apps.ecm.service import algo_handle, notify_event, save_dahua_pic +from apps.hrm.models import Employee +from apps.opm.models import Opl from apps.third.clients import xxClient +from apps.third.clients import dhClient from apps.third.models import TDevice from apps.third.tapis import xxapis from django.utils import timezone +import time +from django.utils import timezone @shared_task @@ -46,7 +51,8 @@ def handle_xx_event_3(name: str, area: Area): 'cate': cate, 'event': event }) - notify_event(event) + voice_msg = area.name + '下有' + str(area.count) + '人,' + cate.name + ',请及时处理' + notify_event(event, voice_msg=voice_msg) @shared_task @@ -67,3 +73,55 @@ def check_event_timeout(): if cate.hanle_minute > 0 and (timezone.now()-i.create_time).seconds > cate.hanle_minute * 60: i.is_timeout = True i.save() + + +@shared_task +def snap_and_analyse(code: str, algo_codes: list, opl_id: str): + global_img_o = dhClient.snap(code) + happen_time = timezone.now() + vchannel = TDevice.objects.filter(code=code).first() + ec_codes = algo_handle(algo_codes, data={}) # 算法处理返回的事件结果 + if ec_codes: + # 获取本次所有发生事件种类 + ecs = EventCate.objects.filter(code__in=ec_codes) + obj_cate = 'other' + ep = None + if 'helmet' in ec_codes: + # 如果存在安全帽事件 + # 尝试以图搜图找到当事人 + res = dhClient.face_search(path=global_img_o) + if res and res[0]: + ep = Employee.objects.filter(id_number=res[0]['identity']).first() + if ep: + obj_cate = 'people' + event = Event() + event.global_img = save_dahua_pic(global_img_o) + if vchannel: + event.vchannel = vchannel + event.area = vchannel.area + event.obj_cate = obj_cate + event.employee = ep + event.happen_time = happen_time + event.opl = Opl.objects.get(id=opl_id) + event.save() + for i in ecs: + Eventdo.objects.get_or_create(cate=i, event=event, defaults={ + 'cate': i, + 'event': event + }) + if event: + notify_event(event) + + +@shared_task +def opl_task(vc_codes: list, opl_id: str): + """作业监控任务 + """ + opl_cate = Opl.objects.get(id=opl_id).cate + # 找到加载的算法 + algo_codes = list(EventCate.objects.filter(opl_cates=opl_cate).values_list('code', flat=True)) + while True: + for i in vc_codes: + snap_and_analyse.delay(i, algo_codes, opl_id) + time.sleep(2) + time.sleep(4) diff --git a/apps/ecm/views.py b/apps/ecm/views.py index b0ab6d00..f2f426dd 100644 --- a/apps/ecm/views.py +++ b/apps/ecm/views.py @@ -75,7 +75,7 @@ class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): perms_map = {'get': '*'} queryset = Event.objects.all() serializer_class = EventSerializer - select_related_fields = ['area', 'employee', 'handle_user'] + select_related_fields = ['area', 'employee', 'handle_user', 'vchannel'] prefetch_related_fields = ['cates'] filterset_class = EventFilterSet diff --git a/apps/hrm/filters.py b/apps/hrm/filters.py index 80ff15d7..04798e48 100755 --- a/apps/hrm/filters.py +++ b/apps/hrm/filters.py @@ -10,7 +10,7 @@ class ClockRecordFilterSet(filters.FilterSet): class Meta: model = ClockRecord - fields = ['employee', 'create_time_start', 'create_time_end', 'year', 'month'] + fields = ['employee', 'create_time_start', 'create_time_end', 'year', 'month', 'type'] def filter_year(self, queryset, name, value): return queryset.filter(create_time_date__year=value) diff --git a/apps/hrm/models.py b/apps/hrm/models.py index 85b818a5..81ef63fb 100755 --- a/apps/hrm/models.py +++ b/apps/hrm/models.py @@ -1,7 +1,7 @@ from django.db import models from apps.system.models import Post, User -from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel +from apps.utils.models import BaseModel, CommonAModel, CommonBModel class Employee(CommonBModel): diff --git a/apps/hrm/serializers.py b/apps/hrm/serializers.py index 553f11a2..c775b10f 100755 --- a/apps/hrm/serializers.py +++ b/apps/hrm/serializers.py @@ -114,7 +114,7 @@ class EmployeeNotWorkRemarkSerializer(ModelSerializer): class ClockRecordListSerializer(serializers.ModelSerializer): - create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + employee_ = EmployeeSimpleSerializer(source='employee', read_only=True) class Meta: model = ClockRecord diff --git a/apps/hrm/services.py b/apps/hrm/services.py index d6f1ce7e..9eed0030 100755 --- a/apps/hrm/services.py +++ b/apps/hrm/services.py @@ -201,7 +201,7 @@ class HrmService: if id_number: ep = Employee.objects.filter(id_number=id_number, type="employee").first() # 如果是内部员工创建上班打卡记录(更新) - if ep and ep.is_at_work is False: + if ep and ep.is_atwork is False: now = timezone.now() cr_10 = ClockRecord.objects.filter(type=10, employee=ep, create_time__year=now.year, create_time__month=now.month, create_time__day=now.day).first() if cr_10: @@ -217,7 +217,8 @@ class HrmService: cr_10.trigger = 'door' cr_10.detail = data['info']['extend'] cr_10.save() - ep.is_at_work = True + ep.is_atwork = True + ep.last_check_time = now ep.save() elif data['info']['extend']['enterOrExit'] == "2": # 如果是出门 @@ -225,7 +226,7 @@ class HrmService: if id_number: ep = Employee.objects.filter(id_number=id_number, type="employee").first() # 如果是内部员工创建下班打卡记录(更新) - if ep and ep.is_at_work: + if ep and ep.is_atwork: now = timezone.now() cr_20 = ClockRecord.objects.filter(type=20, employee=ep, create_time__year=now.year, create_time__month=now.month, create_time__day=now.day).first() if cr_20: @@ -241,5 +242,6 @@ class HrmService: cr_20.trigger = 'door' cr_20.detail = data['info']['extend'] cr_20.save() - ep.is_at_work = False + ep.is_atwork = False + ep.last_check_time = now ep.save() diff --git a/apps/hrm/views.py b/apps/hrm/views.py index b20de223..d998b2c3 100755 --- a/apps/hrm/views.py +++ b/apps/hrm/views.py @@ -208,6 +208,7 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet): permission_classes = [AllowAny] queryset = ClockRecord.objects.all() select_related_fields = ['employee'] + search_fields = ['employee__name', 'employee__number'] serializer_class = ClockRecordListSerializer filterset_class = ClockRecordFilterSet ordering = ['-pk'] diff --git a/apps/opm/migrations/0003_auto_20220812_1715.py b/apps/opm/migrations/0003_auto_20220812_1715.py new file mode 100644 index 00000000..8df43d89 --- /dev/null +++ b/apps/opm/migrations/0003_auto_20220812_1715.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.12 on 2022-08-12 09:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('third', '0009_tlog_errors'), + ('opm', '0002_initial'), + ] + + operations = [ + migrations.AddField( + model_name='operation', + name='cates', + field=models.ManyToManyField(through='opm.Opl', to='opm.OplCate'), + ), + migrations.AddField( + model_name='operation', + name='vchannels', + field=models.ManyToManyField(to='third.TDevice', verbose_name='监控所用摄像头'), + ), + migrations.AddField( + model_name='opl', + name='mtask_id', + field=models.CharField(blank=True, max_length=200, null=True, verbose_name='视频监控任务ID'), + ), + ] diff --git a/apps/opm/models.py b/apps/opm/models.py index 42071fe8..629987e5 100644 --- a/apps/opm/models.py +++ b/apps/opm/models.py @@ -1,5 +1,6 @@ from django.db import models from apps.system.models import User +from apps.third.models import TDevice from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBDModel @@ -56,6 +57,8 @@ class Operation(CommonBDModel): coordinator = models.ForeignKey('system.user', verbose_name='业务部门协调员', on_delete=models.CASCADE) state_work = models.CharField('生产状态', max_length=20, help_text='运行/停机/检修') + cates = models.ManyToManyField(OplCate, through='opm.opl') + vchannels = models.ManyToManyField(TDevice, verbose_name='监控所用摄像头') class Opl(CommonBDModel): @@ -114,6 +117,7 @@ class Opl(CommonBDModel): ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, null=True, blank=True) + mtask_id = models.CharField('视频监控任务ID', max_length=200, null=True, blank=True) class OplWorker(BaseModel): diff --git a/apps/opm/serializers.py b/apps/opm/serializers.py index 7fe1383f..f5b7dfd6 100644 --- a/apps/opm/serializers.py +++ b/apps/opm/serializers.py @@ -60,6 +60,7 @@ class OperationDetailSerializer(CustomModelSerializer): dept_bus_ = DeptSimpleSerializer(source='dept_bus', read_only=True) coordinator_ = UserSimpleSerializer(source='coordinator', read_only=True) create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + cates_ = OplCateSimpleSerializer(source='cates', read_only=True, many=True) class Meta: model = Operation diff --git a/apps/opm/services.py b/apps/opm/services.py index fac1d58f..718cdf96 100644 --- a/apps/opm/services.py +++ b/apps/opm/services.py @@ -1,4 +1,6 @@ +from apps.ecm.tasks import opl_task from apps.opm.models import Operation, Opl, OplWorker +from apps.third.models import TDevice from apps.wf.models import Ticket, Transition @@ -53,13 +55,35 @@ def bind_opl(ticket: Ticket, transition: Transition, new_ticket_data: dict): def opl_audit_end(ticket: Ticket): + # 任务执行1 opl = Opl.objects.get(ticket=ticket) op = opl.operation if op.state == Operation.OP_AUDIT: - op.state = Operation.OP_WORK + op.state = Operation.OP_WAIT op.save() # 授予相关工作人员区域进入权限 # worker_ep_ids = list(OplWorker.objects.filter(opl=opl).values_list('worker__id', flat=True)) # 发送通知 + + +def opl_start(ticket: Ticket): + # 任务执行2 开始许可证作业 + opl = Opl.objects.get(ticket=ticket) + + op = opl.operation + if op.state == Operation.OP_WAIT: + op.state = Operation.OP_WORK + op.save() + + # 给摄像头加载循环拍照算法 + # 找到作业点的摄像头, 如果指定摄像头就用指定的摄像头 + if op.vchannels: + vc_codes = list(op.vchannels.all().values_list('code', flat=True)) + else: + vc_codes = list(TDevice.objects.filter(type=TDevice.DEVICE_VCHANNEL, area=op.area).values_list('code', flat=True)) + opl_id = opl.id + task = opl_task.delay(vc_codes, opl_id) + opl.mtask_id = task.task_id + opl.save() diff --git a/apps/opm/tasks.py b/apps/opm/tasks.py index 6400b302..43a9f07c 100644 --- a/apps/opm/tasks.py +++ b/apps/opm/tasks.py @@ -4,16 +4,25 @@ from apps.opm.models import Operation, Opl from apps.utils.tasks import CustomTask from celery import shared_task -from apps.wf.models import State, Ticket +from apps.wf.models import Ticket @shared_task(base=CustomTask) def opl_end(ticket_id): """ - 作业关闭时执行 + 作业许可证关闭时执行 """ ticket = Ticket.objects.get(id=ticket_id) - operation = ticket.opl.operation + opl = ticket.opl + if opl.mtask_id: + from celery.app.control import Control + from server.celery import app + celery_control = Control(app=app) + # 关闭作业视频监控任务 + celery_control.revoke(opl.mtask_id, terminate=True) + opl.mtask_id = None + opl.save() + operation = opl.operation opls = Opl.objects.filter(operation=operation) opls.filter(ticket=None).delete() # 删除无用许可证 states = opls.values_list('ticket__state__type', flat=True) @@ -24,19 +33,19 @@ def opl_end(ticket_id): operation.save() -@shared_task(base=CustomTask) -def opl_audit_start(ticket_id): - operation = Opl.objects.get(ticket__id=ticket_id).operation - if operation.state == Operation.OP_CREATE: - operation.state = Operation.OP_AUDIT - operation.save() +# @shared_task(base=CustomTask) +# def opl_audit_start(ticket_id): +# operation = Opl.objects.get(ticket__id=ticket_id).operation +# if operation.state == Operation.OP_CREATE: +# operation.state = Operation.OP_AUDIT +# operation.save() -@shared_task(base=CustomTask) -def opl_audit_end(ticket_id): - opl = Opl.objects.get(ticket__id=ticket_id) - operation = opl.operation - if operation.state == Operation.OP_AUDIT: - operation.state = Operation.OP_WORK - operation.save() +# @shared_task(base=CustomTask) +# def opl_audit_end(ticket_id): +# opl = Opl.objects.get(ticket__id=ticket_id) +# operation = opl.operation +# if operation.state == Operation.OP_AUDIT: +# operation.state = Operation.OP_WORK +# operation.save() # 授予区域或围栏权限 diff --git a/apps/opm/views.py b/apps/opm/views.py index 900bba96..8ef2d5e0 100644 --- a/apps/opm/views.py +++ b/apps/opm/views.py @@ -31,6 +31,7 @@ class OperationViewSet(CustomModelViewSet): serializer_class = OperationDetailSerializer retrieve_serializer_class = OperationDetailSerializer select_related_fields = ['area', 'dept_bus', 'dept_ter', 'coordinator'] + prefetch_related_fields = ['cates'] filterset_fields = ['state', 'opl_operation__cate', 'area', 'create_by'] def update(self, request, *args, **kwargs): diff --git a/apps/third/dahua.py b/apps/third/dahua.py index e48f2250..db12b22f 100644 --- a/apps/third/dahua.py +++ b/apps/third/dahua.py @@ -10,7 +10,7 @@ from rest_framework.exceptions import APIException, ParseError from apps.third.errors import DH_REQUEST_ERROR from apps.third.models import Tlog from apps.utils.my_rsa import encrypt_data -from apps.utils.tools import print_roundtrip +from apps.utils.tools import convert_to_base64, print_roundtrip from django.utils.timezone import now from apps.third.tapis import dhapis @@ -135,13 +135,17 @@ class DhClient: 返回完整访问地址 """ json_data = { - "deviceCode": "1000038", - "operation": "generalJsonTransport", - "params": "{\"method\":\"dev.snap\",\"id\":123,\"params\":{\"DevID\":\"1000038\",\"DevChannel\":0,\"PicNum\":1,\"SnapType\":1,\"CmdSrc\":0}}", + "deviceCode": "1000093", + "operation": "generalJsonTransport" } if '$' in code: d_code = code.split('$')[0] json_data['deviceCode'] = d_code + json_data['params'] = '{\"method\":\"dev.snap\",\"id\":123,\"params\":{\"DevID\":\"' + \ + str(d_code) + '\",\"DevChannel\":0,\"PicNum\":1,\"SnapType\":1,\"CmdSrc\":0}}' + _, res = self.request(**dhapis['dev_snap'], json=json_data) + res = json.loads(res) + return self.get_full_pic(res['params']['PicInfo']) def get_password_token(self): _, res = self.request(**dhapis['oauth_key']) @@ -156,3 +160,104 @@ class DhClient: }) _, res2 = self.request(**dhapis['oauth_token'], json=res) return res2 + + def face_search(self, path: str): + """人像库检索 + + Args: + path (str): 图片地址 + + 返回第一个识别结果 + """ + base64img = convert_to_base64(path) + json_data = { + "base64Img": base64img, + "devType": 1, + "deviceCodes": ["1000038"], + "groupIds": ["100001"], + "searchGroupType": 2, + "threshold": "80" + } + _, res = self.request(**dhapis['face_search'], json=json_data) + return res['pageData'] + # 返回的识别结果是从右往左 + """ + "pageData": [ + { + "id": null, + "devId": "1000038", + "channelId": null, + "channelSeq": null, + "channelName": null, + "faceLibId": "1", + "libImgUrl": "6ad010cf-ce45-11ec-9715-e4246c7d1635/20220805/63/dsf_8c2487c1-14a1-11ed-880a-e4246c7d1635_2551349_2624751.jpg", + "faceImgUrl": null, + "hitImgUrl": null, + "identity": "342422199004040175", + "name": "曹前明", + "sex": "男", + "nation": null, + "bornYear": null, + "age": null, + "glasses": null, + "fringe": null, + "similarity": 99, + "recTime": null, + "mask": null, + "beard": null, + "glass": null, + "maskStr": null, + "beardStr": null, + "glassStr": null, + "featureStr": "其他 ", + "groupName": "内部库1", + "groupType": 3, + "groupTypeStr": "内部库", + "identityType": 1, + "identityTypeStr": "身份证", + "birthday": null, + "searchGroupType": 2, + "birthdayStr": "2022-07-14", + "deviceName": "ivs服务器", + "personCode": "123" + }, + { + "id": null, + "devId": "1000038", + "channelId": null, + "channelSeq": null, + "channelName": null, + "faceLibId": "1", + "libImgUrl": "6ad010cf-ce45-11ec-9715-e4246c7d1635/20220805/63/dsf_8c2487c1-14a1-11ed-880a-e4246c7d1635_2624751_2701750.jpg", + "faceImgUrl": null, + "hitImgUrl": null, + "identity": "371324199502156548", + "name": "石静", + "sex": "女", + "nation": null, + "bornYear": null, + "age": null, + "glasses": null, + "fringe": null, + "similarity": 96, + "recTime": null, + "mask": null, + "beard": null, + "glass": null, + "maskStr": null, + "beardStr": null, + "glassStr": null, + "featureStr": "其他 ", + "groupName": "内部库1", + "groupType": 3, + "groupTypeStr": "内部库", + "identityType": 1, + "identityTypeStr": "身份证", + "birthday": null, + "searchGroupType": 2, + "birthdayStr": "2022-06-24", + "deviceName": "ivs服务器", + "personCode": "shijing" + } + ] + """ diff --git a/apps/third/tapis.py b/apps/third/tapis.py index 3f3bbbcc..825040bb 100755 --- a/apps/third/tapis.py +++ b/apps/third/tapis.py @@ -115,6 +115,10 @@ dhapis = { "oauth_token": { "url": "/evo-apigw/evo-oauth/1.0.0/oauth/extend/token", "method": "post" + }, + "face_search": { + "url": "/evo-apigw/evo-face/faceSearch/third/faceSearchSync", + "method": "post" } } diff --git a/apps/third/views.py b/apps/third/views.py index 22ada7ca..2dca8e8d 100755 --- a/apps/third/views.py +++ b/apps/third/views.py @@ -21,13 +21,15 @@ from django.conf import settings class DahuaTestView(MyLoggingMixin, APIView): - """ + """大华测试接口 + 大华测试接口 """ permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): - res = dhClient.get_password_token() + res = dhClient.face_search(path='/media/2022/07/13/three.jpg') + return Response(res) diff --git a/apps/third/views_d.py b/apps/third/views_d.py index 0f88d71c..64aa4edb 100644 --- a/apps/third/views_d.py +++ b/apps/third/views_d.py @@ -204,7 +204,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom json = { "pageNum": 1, "pageSize": 1000, - 'channelTypeList': ["1"] + 'channelTypeList': ["1"], + 'includeSubOwnerCodeFlag': True } _, res = dhClient.request(**dhapis['channel_list'], json=json) if res.get('pageData', None): @@ -229,7 +230,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom json = { "pageNum": 1, "pageSize": 1000, - 'channelTypeList': ["7"] + 'channelTypeList': ["7"], + 'includeSubOwnerCodeFlag': True } _, res = dhClient.request(**dhapis['channel_list'], json=json) if res.get('pageData', None): @@ -302,7 +304,10 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom 视频通道列表 """ - request.data.update({'channelTypeList': ["1"]}) + request.data.update({ + 'channelTypeList': ["1"], + 'includeSubOwnerCodeFlag': True + }) _, res = dhClient.request(**dhapis['channel_list'], json=request.data) codes = [] if res.get('pageData', None): @@ -349,7 +354,10 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom 闸机通道列表 """ - request.data.update({'channelTypeList': ["7"]}) + request.data.update({ + 'channelTypeList': ["7"], + 'includeSubOwnerCodeFlag': True + }) _, res = dhClient.request(**dhapis['channel_list'], json=request.data) codes = [] if res.get('pageData', None): diff --git a/apps/utils/tools.py b/apps/utils/tools.py index e4d9824b..0ad5477b 100755 --- a/apps/utils/tools.py +++ b/apps/utils/tools.py @@ -2,6 +2,10 @@ import textwrap import random import string from datetime import datetime +from django.conf import settings +import base64 +import requests +from io import BytesIO def print_roundtrip(response, *args, **kwargs): @@ -40,6 +44,19 @@ def timestamp_to_time(millis): return datetime.fromtimestamp(millis) +def convert_to_base64(path: str): + """给定图片转base64 + + Args: + path (str): 图片地址 + """ + if path.startswith('http'): # 如果是网络图片 + return str(base64.b64encode(BytesIO(requests.get(url=path).content).read()), 'utf-8') + else: + with open(settings.BASE_DIR + path, 'rb') as f: + return str(base64.b64encode(f.read()), 'utf-8') + + def p_in_poly(p, poly): px = p['x'] py = p['y'] diff --git a/apps/vm/serializers.py b/apps/vm/serializers.py index ab6f5e2c..cf3a2078 100644 --- a/apps/vm/serializers.py +++ b/apps/vm/serializers.py @@ -56,19 +56,23 @@ class VpeopleCreateSerializer(CustomModelSerializer): fields = ['visit', 'visitor', 'is_main'] def create(self, validated_data): - if validated_data['visit'].state != Visit.V_CREATE: + visit = validated_data['visit'] + visitor = validated_data['visitor'] + if visit.state != Visit.V_CREATE: raise ParseError('项目非创建状态不可新增成员') - if validated_data['visit'].level == 10: - visitor = validated_data['visitor'] + if visit.level == 10: if visitor.id_number and visitor.photo: pass else: raise ParseError('访客信息不全,请完善后再试') - if Vpeople.objects.filter(visit=validated_data['visit'], visitor=validated_data['visitor']).exists(): + if Vpeople.objects.filter(visit=visit, visitor=visitor).exists(): raise ParseError('该访客已选中') ins = super().create(validated_data) if ins.is_main: - Vpeople.objects.filter(visit=validated_data['visit']).exclude(id=ins.id).update(is_main=False) + Vpeople.objects.filter(visit=visit).exclude(id=ins.id).update(is_main=False) + if visit.level == 10: + visit.count_people = Vpeople.objects.filter(visit=visit).count() + visit.save() return ins