作业过程中动态执行视频监控

This commit is contained in:
曹前明 2022-08-12 17:17:10 +08:00
parent 811f96fe83
commit 1806eb80bb
25 changed files with 451 additions and 65 deletions

View File

@ -143,6 +143,11 @@ class TestViewSet(CustomGenericViewSet):
ret = {} ret = {}
task = show.delay(*vdata.get('args', []), **vdata.get('kwargs', {})) task = show.delay(*vdata.get('args', []), **vdata.get('kwargs', {}))
ret['task_id'] = task.task_id 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) return Response(ret)
@action(methods=['post'], detail=False, serializer_class=Serializer) @action(methods=['post'], detail=False, serializer_class=Serializer)

View File

@ -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='关联许可证'),
),
]

View File

@ -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='关联许可证'),
),
]

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from apps.am.models import Area from apps.am.models import Area
from apps.hrm.models import Employee from apps.hrm.models import Employee
from apps.opm.models import Opl, OplCate
from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBModel
from apps.system.models import Post, User from apps.system.models import Post, User
from apps.third.models import TDevice from apps.third.models import TDevice
@ -28,6 +29,7 @@ class EventCate(CommonAModel):
default=Area.AREA_1) default=Area.AREA_1)
hanle_minute = models.PositiveSmallIntegerField('处理时间', default=0, help_text='超过处理时间事件状态变为超时未处理,0代表未配置') hanle_minute = models.PositiveSmallIntegerField('处理时间', default=0, help_text='超过处理时间事件状态变为超时未处理,0代表未配置')
same_allow_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): def __str__(self):
return self.name return self.name
@ -83,6 +85,7 @@ class Event(CommonBModel):
area = models.ForeignKey(Area, verbose_name='发生区域', on_delete=models.CASCADE, null=True, blank=True) 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) vchannel = models.ForeignKey(TDevice, verbose_name='抓拍设备', on_delete=models.CASCADE, null=True, blank=True)
location = models.JSONField('事件点位坐标', default=dict, null=False, 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(人员)/...') obj_cate = models.CharField('发生对象', max_length=20, help_text='people(人员)/...')
employee = models.ForeignKey(Employee, verbose_name='当事人', employee = models.ForeignKey(Employee, verbose_name='当事人',
on_delete=models.CASCADE, null=True, blank=True) on_delete=models.CASCADE, null=True, blank=True)

View File

@ -78,6 +78,7 @@ class EventSerializer(serializers.ModelSerializer):
area_ = AreaSerializer(source='area', read_only=True) area_ = AreaSerializer(source='area', read_only=True)
cates_ = EventCateSimpleSerializer(source='cates', read_only=True, many=True) cates_ = EventCateSimpleSerializer(source='cates', read_only=True, many=True)
employee_ = EmployeeSerializer(source='employee', read_only=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) handle_user_name = serializers.CharField(source='handle_user.name', read_only=True)
face_img = MyFilePathField() face_img = MyFilePathField()
global_img = MyFilePathField() global_img = MyFilePathField()

View File

@ -77,7 +77,7 @@ def algo_handle(codes: list, data: dict):
return ['helmet'] return ['helmet']
def notify_event(event: Event): def notify_event(event: Event, voice_msg=''):
"""事件后续处理: """事件后续处理:
Args: Args:
@ -87,7 +87,7 @@ def notify_event(event: Event):
ep = event.employee ep = event.employee
obj_cate = event.obj_cate obj_cate = event.obj_cate
params = {'area': event.area.name, 'employee': '', 'event': ''} params = {'area': event.area.name, 'employee': '', 'event': ''}
if obj_cate == 'people' and ep: if ep:
ep_name = ep.name ep_name = ep.name
ep_type = '员工' ep_type = '员工'
if ep.type == 'rempoyee': if ep.type == 'rempoyee':
@ -97,11 +97,14 @@ def notify_event(event: Event):
params['employee'] = ep_type + ep_name params['employee'] = ep_type + ep_name
elif obj_cate == 'people': elif obj_cate == 'people':
params['employee'] = '未知人员' params['employee'] = '未知人员'
elif obj_cate == 'area':
params['employee'] = '区域'
for i in event.cates.all(): for i in event.cates.all():
params['event'] = params['event'] + i.name + ',' 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() event.save()
# 喇叭播放 # 喇叭播放
Thread(target=save_voice_and_speak, args=(event,), daemon=True).start() 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 v_p, v_num = main_cate.voice_person, main_cate.voice_num
_, event.voice, _ = generate_voice(event.voice_msg, v_p) _, event.voice, _ = generate_voice(event.voice_msg, v_p)
event.save() event.save()
if event.area: # 如果事件存在发生区域 if main_cate.speaker_on:
sps = list(TDevice.objects.filter(area=event.area, type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True)) if event.area: # 如果事件存在发生区域
if len(sps) == 0: # 如果当前区域没有喇叭就找覆盖区的喇叭 sps = list(TDevice.objects.filter(area=event.area, type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True))
sps = list(TDevice.objects.filter(areas=event.area, if len(sps) == 0: # 如果当前区域没有喇叭就找覆盖区的喇叭
type=TDevice.DEVICE_SPEAKER).values_list('code', flat=True)) 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(): for m in event.cates.all():
if n.code not in sps: for n in m.speakers.all():
sps.append(n.code) if n.code not in sps:
if sps: sps.append(n.code)
spClient.speak(event.voice, sps, v_num) if sps:
spClient.speak(event.voice, sps, v_num)
def create_remind(event: Event, params: dict): def create_remind(event: Event, params: dict):
@ -221,10 +225,10 @@ def dispatch_dahua_event(data: dict):
event = None event = None
if alarm_type in [1001003, 1001000] and vchannel: # 内部人员/或陌生人报警 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)) algo__code=None).order_by('algo__priority', 'algo__create_time').values_list('algo__code', flat=True))
area = vchannel.area # 视频所在区域 area = vchannel.area # 视频所在区域
if algo_codes and area: # 如果加载了算法且视频通道绑定区域才继续 if algo_codes: # 如果加载了算法才继续
face_img_o = dhClient.get_full_pic(data['info']['alarmPicture']) face_img_o = dhClient.get_full_pic(data['info']['alarmPicture'])
global_img_o = dhClient.get_full_pic(data['info']['extend']['globalScenePicUrl']) global_img_o = dhClient.get_full_pic(data['info']['extend']['globalScenePicUrl'])
obj_cate = 'people' obj_cate = 'people'
@ -250,6 +254,59 @@ def dispatch_dahua_event(data: dict):
'cate': i, 'cate': i,
'event': event '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: if event:
notify_event(event) notify_event(event)
@ -450,7 +507,7 @@ def blt_online(data):
blts = TDevice.objects.filter(code=data['userId']).first() blts = TDevice.objects.filter(code=data['userId']).first()
if blts.employee: if blts.employee:
ep = 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() now = timezone.now()
cr_10 = ClockRecord.objects.filter(type=10, employee=ep, create_time__year=now.year, 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.trigger = 'location'
cr_10.detail = data cr_10.detail = data
cr_10.save() cr_10.save()
ep.is_at_work = True ep.is_atwork = True
ep.last_check_time = now
ep.save() ep.save()
@ -477,7 +535,7 @@ def blt_offline(data):
# 定位离线 # 定位离线
if blts.employee: if blts.employee:
ep = 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() now = timezone.now()
cr_20 = ClockRecord.objects.filter(type=20, employee=ep, create_time__year=now.year, 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.trigger = 'location'
cr_20.detail = data cr_20.detail = data
cr_20.save() cr_20.save()
ep.is_at_work = False ep.is_atwork = False
ep.last_check_time = now
ep.save() ep.save()

View File

@ -6,11 +6,16 @@ from celery import shared_task
from apps.am.models import Area from apps.am.models import Area
from apps.ecm.models import EventCate, Eventdo 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 xxClient
from apps.third.clients import dhClient
from apps.third.models import TDevice from apps.third.models import TDevice
from apps.third.tapis import xxapis from apps.third.tapis import xxapis
from django.utils import timezone from django.utils import timezone
import time
from django.utils import timezone
@shared_task @shared_task
@ -46,7 +51,8 @@ def handle_xx_event_3(name: str, area: Area):
'cate': cate, 'cate': cate,
'event': event 'event': event
}) })
notify_event(event) voice_msg = area.name + '下有' + str(area.count) + '人,' + cate.name + ',请及时处理'
notify_event(event, voice_msg=voice_msg)
@shared_task @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: if cate.hanle_minute > 0 and (timezone.now()-i.create_time).seconds > cate.hanle_minute * 60:
i.is_timeout = True i.is_timeout = True
i.save() 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)

View File

@ -75,7 +75,7 @@ class EventViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
perms_map = {'get': '*'} perms_map = {'get': '*'}
queryset = Event.objects.all() queryset = Event.objects.all()
serializer_class = EventSerializer serializer_class = EventSerializer
select_related_fields = ['area', 'employee', 'handle_user'] select_related_fields = ['area', 'employee', 'handle_user', 'vchannel']
prefetch_related_fields = ['cates'] prefetch_related_fields = ['cates']
filterset_class = EventFilterSet filterset_class = EventFilterSet

View File

@ -10,7 +10,7 @@ class ClockRecordFilterSet(filters.FilterSet):
class Meta: class Meta:
model = ClockRecord 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): def filter_year(self, queryset, name, value):
return queryset.filter(create_time_date__year=value) return queryset.filter(create_time_date__year=value)

View File

@ -1,7 +1,7 @@
from django.db import models from django.db import models
from apps.system.models import Post, User 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): class Employee(CommonBModel):

View File

@ -114,7 +114,7 @@ class EmployeeNotWorkRemarkSerializer(ModelSerializer):
class ClockRecordListSerializer(serializers.ModelSerializer): class ClockRecordListSerializer(serializers.ModelSerializer):
create_by_ = UserSimpleSerializer(source='create_by', read_only=True) employee_ = EmployeeSimpleSerializer(source='employee', read_only=True)
class Meta: class Meta:
model = ClockRecord model = ClockRecord

View File

@ -201,7 +201,7 @@ class HrmService:
if id_number: if id_number:
ep = Employee.objects.filter(id_number=id_number, type="employee").first() 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() 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() 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: if cr_10:
@ -217,7 +217,8 @@ class HrmService:
cr_10.trigger = 'door' cr_10.trigger = 'door'
cr_10.detail = data['info']['extend'] cr_10.detail = data['info']['extend']
cr_10.save() cr_10.save()
ep.is_at_work = True ep.is_atwork = True
ep.last_check_time = now
ep.save() ep.save()
elif data['info']['extend']['enterOrExit'] == "2": elif data['info']['extend']['enterOrExit'] == "2":
# 如果是出门 # 如果是出门
@ -225,7 +226,7 @@ class HrmService:
if id_number: if id_number:
ep = Employee.objects.filter(id_number=id_number, type="employee").first() 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() 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() 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: if cr_20:
@ -241,5 +242,6 @@ class HrmService:
cr_20.trigger = 'door' cr_20.trigger = 'door'
cr_20.detail = data['info']['extend'] cr_20.detail = data['info']['extend']
cr_20.save() cr_20.save()
ep.is_at_work = False ep.is_atwork = False
ep.last_check_time = now
ep.save() ep.save()

View File

@ -208,6 +208,7 @@ class ClockRecordViewSet(ListModelMixin, CustomGenericViewSet):
permission_classes = [AllowAny] permission_classes = [AllowAny]
queryset = ClockRecord.objects.all() queryset = ClockRecord.objects.all()
select_related_fields = ['employee'] select_related_fields = ['employee']
search_fields = ['employee__name', 'employee__number']
serializer_class = ClockRecordListSerializer serializer_class = ClockRecordListSerializer
filterset_class = ClockRecordFilterSet filterset_class = ClockRecordFilterSet
ordering = ['-pk'] ordering = ['-pk']

View File

@ -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'),
),
]

View File

@ -1,5 +1,6 @@
from django.db import models from django.db import models
from apps.system.models import User from apps.system.models import User
from apps.third.models import TDevice
from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBDModel from apps.utils.models import BaseModel, CommonADModel, CommonAModel, CommonBDModel
@ -56,6 +57,8 @@ class Operation(CommonBDModel):
coordinator = models.ForeignKey('system.user', verbose_name='业务部门协调员', coordinator = models.ForeignKey('system.user', verbose_name='业务部门协调员',
on_delete=models.CASCADE) on_delete=models.CASCADE)
state_work = models.CharField('生产状态', max_length=20, help_text='运行/停机/检修') 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): class Opl(CommonBDModel):
@ -114,6 +117,7 @@ class Opl(CommonBDModel):
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单', ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
on_delete=models.CASCADE, on_delete=models.CASCADE,
null=True, blank=True) null=True, blank=True)
mtask_id = models.CharField('视频监控任务ID', max_length=200, null=True, blank=True)
class OplWorker(BaseModel): class OplWorker(BaseModel):

View File

@ -60,6 +60,7 @@ class OperationDetailSerializer(CustomModelSerializer):
dept_bus_ = DeptSimpleSerializer(source='dept_bus', read_only=True) dept_bus_ = DeptSimpleSerializer(source='dept_bus', read_only=True)
coordinator_ = UserSimpleSerializer(source='coordinator', read_only=True) coordinator_ = UserSimpleSerializer(source='coordinator', read_only=True)
create_by_ = UserSimpleSerializer(source='create_by', read_only=True) create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
cates_ = OplCateSimpleSerializer(source='cates', read_only=True, many=True)
class Meta: class Meta:
model = Operation model = Operation

View File

@ -1,4 +1,6 @@
from apps.ecm.tasks import opl_task
from apps.opm.models import Operation, Opl, OplWorker from apps.opm.models import Operation, Opl, OplWorker
from apps.third.models import TDevice
from apps.wf.models import Ticket, Transition 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): def opl_audit_end(ticket: Ticket):
# 任务执行1
opl = Opl.objects.get(ticket=ticket) opl = Opl.objects.get(ticket=ticket)
op = opl.operation op = opl.operation
if op.state == Operation.OP_AUDIT: if op.state == Operation.OP_AUDIT:
op.state = Operation.OP_WORK op.state = Operation.OP_WAIT
op.save() op.save()
# 授予相关工作人员区域进入权限 # 授予相关工作人员区域进入权限
# worker_ep_ids = list(OplWorker.objects.filter(opl=opl).values_list('worker__id', flat=True)) # 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()

View File

@ -4,16 +4,25 @@ from apps.opm.models import Operation, Opl
from apps.utils.tasks import CustomTask from apps.utils.tasks import CustomTask
from celery import shared_task from celery import shared_task
from apps.wf.models import State, Ticket from apps.wf.models import Ticket
@shared_task(base=CustomTask) @shared_task(base=CustomTask)
def opl_end(ticket_id): def opl_end(ticket_id):
""" """
作业关闭时执行 作业许可证关闭时执行
""" """
ticket = Ticket.objects.get(id=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 = Opl.objects.filter(operation=operation)
opls.filter(ticket=None).delete() # 删除无用许可证 opls.filter(ticket=None).delete() # 删除无用许可证
states = opls.values_list('ticket__state__type', flat=True) states = opls.values_list('ticket__state__type', flat=True)
@ -24,19 +33,19 @@ def opl_end(ticket_id):
operation.save() operation.save()
@shared_task(base=CustomTask) # @shared_task(base=CustomTask)
def opl_audit_start(ticket_id): # def opl_audit_start(ticket_id):
operation = Opl.objects.get(ticket__id=ticket_id).operation # operation = Opl.objects.get(ticket__id=ticket_id).operation
if operation.state == Operation.OP_CREATE: # if operation.state == Operation.OP_CREATE:
operation.state = Operation.OP_AUDIT # operation.state = Operation.OP_AUDIT
operation.save() # operation.save()
@shared_task(base=CustomTask) # @shared_task(base=CustomTask)
def opl_audit_end(ticket_id): # def opl_audit_end(ticket_id):
opl = Opl.objects.get(ticket__id=ticket_id) # opl = Opl.objects.get(ticket__id=ticket_id)
operation = opl.operation # operation = opl.operation
if operation.state == Operation.OP_AUDIT: # if operation.state == Operation.OP_AUDIT:
operation.state = Operation.OP_WORK # operation.state = Operation.OP_WORK
operation.save() # operation.save()
# 授予区域或围栏权限 # 授予区域或围栏权限

View File

@ -31,6 +31,7 @@ class OperationViewSet(CustomModelViewSet):
serializer_class = OperationDetailSerializer serializer_class = OperationDetailSerializer
retrieve_serializer_class = OperationDetailSerializer retrieve_serializer_class = OperationDetailSerializer
select_related_fields = ['area', 'dept_bus', 'dept_ter', 'coordinator'] select_related_fields = ['area', 'dept_bus', 'dept_ter', 'coordinator']
prefetch_related_fields = ['cates']
filterset_fields = ['state', 'opl_operation__cate', 'area', 'create_by'] filterset_fields = ['state', 'opl_operation__cate', 'area', 'create_by']
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):

View File

@ -10,7 +10,7 @@ from rest_framework.exceptions import APIException, ParseError
from apps.third.errors import DH_REQUEST_ERROR from apps.third.errors import DH_REQUEST_ERROR
from apps.third.models import Tlog from apps.third.models import Tlog
from apps.utils.my_rsa import encrypt_data 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 django.utils.timezone import now
from apps.third.tapis import dhapis from apps.third.tapis import dhapis
@ -135,13 +135,17 @@ class DhClient:
返回完整访问地址 返回完整访问地址
""" """
json_data = { json_data = {
"deviceCode": "1000038", "deviceCode": "1000093",
"operation": "generalJsonTransport", "operation": "generalJsonTransport"
"params": "{\"method\":\"dev.snap\",\"id\":123,\"params\":{\"DevID\":\"1000038\",\"DevChannel\":0,\"PicNum\":1,\"SnapType\":1,\"CmdSrc\":0}}",
} }
if '$' in code: if '$' in code:
d_code = code.split('$')[0] d_code = code.split('$')[0]
json_data['deviceCode'] = d_code 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): def get_password_token(self):
_, res = self.request(**dhapis['oauth_key']) _, res = self.request(**dhapis['oauth_key'])
@ -156,3 +160,104 @@ class DhClient:
}) })
_, res2 = self.request(**dhapis['oauth_token'], json=res) _, res2 = self.request(**dhapis['oauth_token'], json=res)
return res2 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"
}
]
"""

View File

@ -115,6 +115,10 @@ dhapis = {
"oauth_token": { "oauth_token": {
"url": "/evo-apigw/evo-oauth/1.0.0/oauth/extend/token", "url": "/evo-apigw/evo-oauth/1.0.0/oauth/extend/token",
"method": "post" "method": "post"
},
"face_search": {
"url": "/evo-apigw/evo-face/faceSearch/third/faceSearchSync",
"method": "post"
} }
} }

View File

@ -21,13 +21,15 @@ from django.conf import settings
class DahuaTestView(MyLoggingMixin, APIView): class DahuaTestView(MyLoggingMixin, APIView):
""" """大华测试接口
大华测试接口 大华测试接口
""" """
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs): 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) return Response(res)

View File

@ -204,7 +204,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
json = { json = {
"pageNum": 1, "pageNum": 1,
"pageSize": 1000, "pageSize": 1000,
'channelTypeList': ["1"] 'channelTypeList': ["1"],
'includeSubOwnerCodeFlag': True
} }
_, res = dhClient.request(**dhapis['channel_list'], json=json) _, res = dhClient.request(**dhapis['channel_list'], json=json)
if res.get('pageData', None): if res.get('pageData', None):
@ -229,7 +230,8 @@ class TDeviceViewSet(ListModelMixin, UpdateModelMixin, DestroyModelMixin, Custom
json = { json = {
"pageNum": 1, "pageNum": 1,
"pageSize": 1000, "pageSize": 1000,
'channelTypeList': ["7"] 'channelTypeList': ["7"],
'includeSubOwnerCodeFlag': True
} }
_, res = dhClient.request(**dhapis['channel_list'], json=json) _, res = dhClient.request(**dhapis['channel_list'], json=json)
if res.get('pageData', None): 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) _, res = dhClient.request(**dhapis['channel_list'], json=request.data)
codes = [] codes = []
if res.get('pageData', None): 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) _, res = dhClient.request(**dhapis['channel_list'], json=request.data)
codes = [] codes = []
if res.get('pageData', None): if res.get('pageData', None):

View File

@ -2,6 +2,10 @@ import textwrap
import random import random
import string import string
from datetime import datetime from datetime import datetime
from django.conf import settings
import base64
import requests
from io import BytesIO
def print_roundtrip(response, *args, **kwargs): def print_roundtrip(response, *args, **kwargs):
@ -40,6 +44,19 @@ def timestamp_to_time(millis):
return datetime.fromtimestamp(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): def p_in_poly(p, poly):
px = p['x'] px = p['x']
py = p['y'] py = p['y']

View File

@ -56,19 +56,23 @@ class VpeopleCreateSerializer(CustomModelSerializer):
fields = ['visit', 'visitor', 'is_main'] fields = ['visit', 'visitor', 'is_main']
def create(self, validated_data): 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('项目非创建状态不可新增成员') raise ParseError('项目非创建状态不可新增成员')
if validated_data['visit'].level == 10: if visit.level == 10:
visitor = validated_data['visitor']
if visitor.id_number and visitor.photo: if visitor.id_number and visitor.photo:
pass pass
else: else:
raise ParseError('访客信息不全,请完善后再试') 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('该访客已选中') raise ParseError('该访客已选中')
ins = super().create(validated_data) ins = super().create(validated_data)
if ins.is_main: 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 return ins