panlei
2019-07-03 15e5410c4fab8b9041729ad9a6efaea7c66d5fae
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
package ruleserver
 
import (
    "encoding/json"
    "fmt"
    "log"
    "ruleprocess/cache"
    "sort"
    "strconv"
    "strings"
    "time"
 
    "basic.com/pubsub/protomsg.git"
    "github.com/knetic/govaluate"
)
 
// 任务
type Task struct {
    camID     string //摄像机ID
    taskID    string //任务ID
    sdkID     string //算法ID
    areaId    string //区域id
    areaName  string //区域名称
    topicType string //规则主题类型,目标/持续时间/灵敏度等
}
 
// 数据库中的规则元素
type SingleRule struct {
    Task
    operatorType     string // 操作符,>=,==...
    compareType      string // 对比类型,值,被选项
    compareValue     string // 对比的值
    ruleWithPrevious string // 跟上一条的逻辑关系
    groupId          string // 大规则id
}
 
// 数据库中单条子规则  跟数据库映射的
type CameraTaskArg struct {
    Id               string `json:"id"`
    CameraTaskId     string `json:"camera_task_id"`
    CameraId         string `json:"camera_id"`
    PolygonId        string `json:"polygon_id"`
    SdkId            string `json:"sdk_id"`
    SdkArgAlias      string `json:"sdk_arg_alias"`
    Operator         string `json:"operator"`      //操作符,>=,==...
    OperatorType     string `json:"operator_type"` //对比类型,值,被选项
    SdkArgValue      string `json:"sdk_arg_value"` //对比的值
    Sort             int    `json:"sort"`
    RuleWithPrevious string `json:"rule_with_previous"` //跟上一条的逻辑关系
    GroupId          string `json:"group_id"`           //大规则id
}
 
// sdk输出的图片提取参数数据之后的数据集合,也是传给下一步用于赋值的数据集合
type AreaMapList struct {
    areaMapList []AreaMap
}
 
// 每个目标的参数:相似度,占比,尺寸
type Arg struct {
    Score      float64 // 区域内的目标的相似度
    Proportion float64 // 区域内的目标的占比
    Size       float64 // 区域内的目标的尺寸
    IsYolo     bool    // 是否是yolo数据
    Location   Rect    // 记下每个目标的位置参数,最后给结果装配人脸数据的时候用的到
    SdkName    string
    ThftRes    protomsg.ThftResult
    Liker      []*protomsg.Baseinfo
}
 
type LikePerson struct {
    Id    string  // 与之相似的底库人员的id
    Score float64 // 与底库人员的相似值
}
 
// 每个区域内的图片数据集合
type AreaMap struct {
    cameraId      string
    areaId        string
    groupId       string
    taskId        string
    sdkId         string
    sdkName       string
    areaJson      string
    triggerLine   string
    directionLine string
    targetNum     int    // 区域内目标数量
    args          []Arg  // 区域内目标集合
    filterData    []Arg  // 过滤后区域内目标集合
    time          string // 当前时间(用以匹配时间规则)
    keepRight     bool   // 是否靠右行
    isStatic      bool   // 是否静止
}
 
// sdk输出的图片上单个目标的数据
type PhotoMap struct {
    Rects   Rect    // 矩形区域参数
    Score   float64 // 相似度得分(有多大程度像一个目标。人脸,人体或车等等)
    IsYolo  bool    // 是否是yolo数据
    SdkName string
    ThftRes protomsg.ThftResult
    Liker   []*protomsg.Baseinfo // 如果是人脸的话尤其是比对,应存下他跟底库的人员的相似情况 yolo的话给nil就行
}
 
// 每个算法对于当前帧画面自己提取的数据
type SdkData struct {
    TaskId      string
    SdkId       string
    SdkName     string
    IsYolo      bool
    Photo       []PhotoMap // yolo算法结构,也可以存人脸的数据,毕竟人脸中能用规则来测的还是那些参数
    KeepRight   bool       // 是否靠右行 算法判断的与上一帧图像的比较结果
    IsStatic    bool       // 是否静止
    ImageWidth  int        // 摄像机拍摄的图像宽 像素
    ImageHeight int        // 摄像机拍摄的图像高 像素
    AreaMapList []AreaMap // 本sdk提取的数据按照区域划分后的数据集合
}
 
// 从算法模块儿拿来的对一帧图像各个算法提取的数据集合
type SdkDatas struct {
    CameraId   string
    Sdkdata    []*SdkData
    RuleResult map[string]interface{} // 过完规则后打的标签 face: []Arg  yolo: []Result
}
 
// 将传递过来的参数转化为
//protomsg.SdkMessage.TaskLabel.SdkmsgWithTask.sdkdata
 
type ResultMsg struct {
    protomsg.SdkMessage
    RuleResult map[string]interface{} // 过完规则后打的标签
}
 
// 过规则库打上的标签
type Result struct {
    TaskId      string // 任务id
    SdkName     string
    RuleGroupId string // 规则组id
    AlarmLevel  int32  // 报警等级
    RuleText    string // 文字版规则组
}
type LittleRuleResult struct {
    SdkName string // 记录下此结果是哪个sdk的结果
    Result  string // 已包含了前置连接符
    Sort    int32
}
 
//
type Face struct {
    Location Rect // 人脸坐标框
    SdkName  string
    ThftRes  protomsg.ThftResult
    Liker    []LikePerson // 相似人员(如果是单纯的人脸检测可无此项)
}
 
// 包含N条规则元素的一整条规则
type CompleteRule struct {
    rule string
}
 
// 根据摄像机id拿到摄像机所有区域
func GetPolygons(cameraId string) []protomsg.CameraPolygon {
    //var api dbapi.CameraApi
    //data := api.FindAllPolygons()
    ////fmt.Println("查到的所有区域:", data)
    //// 根据id从map中拿到区域
    var cameraPolygons []protomsg.CameraPolygon
    //for _, item := range data {
    //    if item.CameraId == cameraId && item.Type != "line" {
    //        // 需要根据比例把前台画的区域的坐标转化为相应摄像机拍摄的图像的大小   x坐标分别*image.width/页面区域宽  y坐标分别*image.height/页面区域高
    //        // 前台宽高固定
    //        //log.Println("------------符合条件的区域:",item)
    //        cameraPolygons = append(cameraPolygons, item)
    //    }
    //}
    cameraPolygons = cache.GetPolygonsByCameraId(cameraId)
 
    //log.Println("根据摄像机id查到的区域", cameraPolygons, "--区域数量为:", len(cameraPolygons))
    return cameraPolygons
}
 
// 把sdk从数据帧上提取的按照区域分类归置
func SdkDataFormat(cameraId string, arg *SdkData, cameraPolygons []protomsg.CameraPolygon) {
    for _, polygon := range cameraPolygons {
        //log.Println("++++++在这儿看一下区域啊:", polygon.Polygon)
        areaMap := AreaMap{cameraId: cameraId, areaId: polygon.Id, areaJson: polygon.Polygon, triggerLine: polygon.TriggerLine, directionLine: polygon.DirectionLine}
        // 为每个摄像机区域填充数据
        areaMap.CountAreaObjs(arg)
        arg.AreaMapList = append(arg.AreaMapList, areaMap)
    }
}
 
// 对单帧图像的判断 是舍弃(或者说对于某些需求可以放ES数据库一份)还是返回
func Judge(args *SdkDatas) {
    if len(args.Sdkdata) > 0 {
        // 拿到本摄像机的区域
        cameraPolygons := GetPolygons(args.CameraId)
        // 把所有的sdk提取的数据都按所属摄像机的区域归置
        for _, arg := range args.Sdkdata {
            SdkDataFormat(args.CameraId, arg, cameraPolygons)
        }
        // 跑本摄像机的所有规则组 一组一组跑
        taskRuleList := GetRuleGroup(args.CameraId) // 本摄像机下所有任务组
        //log.Println("看下摄像机下的任务组:",taskRuleList)
        // 得到属于该摄像机的若干组任务的完整规则(跟每一条完整规则比较之后得出本张图像对于某个规则是否报警的结果。放进map,比如本帧图像的id,所碰撞成功的规则id)
        args.RuleResult = make(map[string]interface{})
        args.RuleResult["yolo"] = []Result{}
        args.RuleResult["face"] = []Arg{}
        if len(taskRuleList) > 0 {
            for _, taskRule := range taskRuleList {
                ruleList := taskRule.GroupRules // 获取的是task下面的任务组
                taskId := taskRule.TaskId
                //log.Println("------------本组任务下的规则组的数量:",len(ruleList))
                for i := 0; i < len(ruleList); i++ {
                    temp := ruleList[i].Rules // temp为一组完整规则 在此需要判断规则是否是联动规则
                    if len(temp) > 0 {
                        if ruleList[i].SetType == "linkTask" {
                            // groupId中含有link则为联动任务
                            linkTask(args, ruleList[i], taskId)
                        } else {
                            // 独立任务的处理
                            RunRule(args, ruleList[i], taskId)
                        }
                    }
                }
            }
        }
    }
}
func RunRule(args *SdkDatas, groupRule *protomsg.GroupRule, taskId string) bool {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("比对规则有误", err.(string))
        }
 
    }()
    log.Println("+++++++++++规则开始运行+++++++++++++++++当前规则--:",*groupRule)
    resultSplice := []*LittleRuleResult{}
    // 先过完条件规则
    for j := 0; j < len(groupRule.Rules); j++ {
        for _, sdkData := range args.Sdkdata {
            for _, areaMap := range sdkData.AreaMapList {
                ruleResult := filterRule(groupRule.Rules[j], &areaMap)
                if ruleResult.Result != "" {
                    log.Println("条件规则结果:", ruleResult.Result)
                    resultSplice = append(resultSplice, &ruleResult)
                }
            }
        }
    }
    // 过完条件数据后把符合条件的人脸数据塞进结果标签里
    faces := []Arg{}
    for _, sdkData := range args.Sdkdata {
        for _, areaMap := range sdkData.AreaMapList {
            for j := 0; j < len(groupRule.Rules); j++ {
                faces = append(faces,putFaceToResult(groupRule.Rules[j], &areaMap)...)
            }
        }
    }
    args.RuleResult["face"] = faces
    // 再过其他数据 这步直接得到结果(真或假) 过目标数量
    for j := 0; j < len(groupRule.Rules); j++ {
        for _, sdkData := range args.Sdkdata {
            for _, areaMap := range sdkData.AreaMapList {
                ruleResult := transferParameters(groupRule.Rules[j], &areaMap)
                if ruleResult.Result != "" {
                    log.Println("数量规则结果:", ruleResult.Result)
                    resultSplice = append(resultSplice, &ruleResult)
                }
            }
        }
    }
    // 这步过的是时间规则(时间段等)
    for j := 0; j < len(groupRule.Rules); j++ {
        for _, sdkData := range args.Sdkdata {
            for _, areaMap := range sdkData.AreaMapList {
                ruleResult := timeRuleResult(groupRule.Rules[j], &areaMap)
                if ruleResult.Result != "" {
                    log.Println("时间规则结果:", ruleResult.Result)
                    resultSplice = append(resultSplice, &ruleResult)
                }
            }
        }
    }
    // 最后过持续时间等时间维度的条件
    for j := 0; j < len(groupRule.Rules); j++ {
        for _, sdkData := range args.Sdkdata {
            for _, areaMap := range sdkData.AreaMapList {
                duration(groupRule.Rules[j], &areaMap)
            }
        }
    }
    // 将数组按sort排序
    sort.Sort(resultList(resultSplice))
    // 排序后取各自的结果和连接符拼出规则表达式得出结果
    completeFormula := ""
    for _, va := range resultSplice {
        completeFormula = completeFormula + va.Result
    }
    if completeFormula != "" {
        log.Println("看看公式-----------:", completeFormula)
        expression, _ := govaluate.NewEvaluableExpression(completeFormula)
        if strings.HasPrefix(completeFormula,"&&") || strings.HasPrefix(completeFormula,"||"){
            panic("规则有误,得到的数学公式不可解析")
        }
        result,_ := expression.Evaluate(nil) // 得到数学公式的结果
        if !result.(bool) {
            // 给这帧数据打上规则组标签
            //args.RuleResult = append(args.RuleResult, Result{TaskId: taskId, RuleGroupId: groupRule.GroupId})
            for k, timeEle := range TimeEleList {
                if strings.Contains(k, taskId) {
                    timeEle.N = timeEle.InitN // 重置定时器
                }
            }
            return false
        } else {
            // 去看池子里是否有与本帧数据有关的定时器,如果有,看此时是否走到0,没有此定时器或有定时器走到0的话返回成功报警
            var flag bool = true
            for k, timeEle := range TimeEleList {
                if strings.Contains(k, taskId) {
                    if timeEle.N != 0 { // 跟这个任务有关的定时器要全部等于0
                        log.Println("———————————-------------不冤,你是被定时器打败的:")
                        flag = false
                    }
                }
            }
            if flag {
                fmt.Println("----------------------------终于走完万里长征")
                // 如果成功了我应该找到规则中涉及到的sdk,记录下他们的sdkname
                sdkName := ""
                for j := 0; j < len(groupRule.Rules); j++ {
                    for _, sdkData := range args.Sdkdata {
                        if groupRule.Rules[j].SdkId == sdkData.SdkId && sdkData.IsYolo && !strings.Contains(sdkName,sdkData.SdkName) { // 只记录yolo符合规则的sdk 同时要避免重复拼接
                            sdkName = sdkData.SdkName + " "
                        }
                    }
                }
                if sdkName != "" {
                    args.RuleResult["yolo"] = append(args.RuleResult["yolo"].([]Result), Result{taskId, sdkName, groupRule.GroupId, groupRule.AlarmLevel, groupRule.GroupText})
                    log.Println("-------------------yolo结果标签",args.RuleResult["yolo"].([]Result))
                }
                if args.RuleResult["face"] != nil {
                    log.Println("-------------------face结果标签",args.RuleResult["face"].([]Arg))
                }
                return true
            } else {
                return false
            }
        }
    } else {
        return false
    }
}
 
func putFaceToResult(rule *protomsg.Rule, am *AreaMap) []Arg {
    faces := []Arg{}
    if rule.SdkId == am.sdkId {
        if len(am.filterData) > 0 {
            for _, data := range am.filterData {
                faces = append(faces, data)
            }
        }
    }
    //log.Println("-----------------------------------------------听说你是空的?",faces)
    return faces
}
 
// 计算区域内的目标数量以及将相似度、占比、尺寸等打包
func (a *AreaMap) CountAreaObjs(arg *SdkData) {
 
    a.targetNum = 0
    threshold := 0.0       // 相似度
    intersectionper := 0.2 // 占比
    size := 0.0            // 尺寸
 
    areaPoints := Json2points(a.areaJson)
    widthScale := float64(arg.ImageWidth / 960)
    heigthScale := float64(arg.ImageHeight / 540)
    // for _, sdkInfo := range arg.SdkMessage.Tasklab.Sdkinfos {
    //     if sdkInfo.Sdktype == "yolo" {
    //         //sdkInfo.Sdkdata.
    //     }
 
    // }
    for _, obj := range arg.Photo {
        log.Println("------------------看看sdkData:",arg.SdkName,"的Photo数据----------------", obj, "----顺便看看占比-----:", PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale))
        if threshold <= obj.Score && size <= float64(obj.Rects.Width*obj.Rects.Height) && intersectionper <= PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale) {
            // 这步要备齐表达式里所需要的所有参数
            a.targetNum++
            arg1 := Arg{obj.Score, PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale), float64(obj.Rects.Width * obj.Rects.Height), obj.IsYolo, obj.Rects, obj.SdkName, obj.ThftRes, obj.Liker}
            //log.Println("放进去的arg:-------", arg1)
            a.args = append(a.args, arg1)
            a.filterData = append(a.filterData, arg1)
        }
    }
    a.sdkId = arg.SdkId
    a.sdkName = arg.SdkName
    a.time = time.Unix(time.Now().Unix(), 0).String()[11:16]
    a.keepRight = arg.KeepRight
    a.isStatic = arg.IsStatic
    //log.Println("--------------------看看区域数据:",*a)
}
 
// 将字符串格式的坐标序列化为Point格式
func Json2points(areaPoints string) []Point {
    var pts []Point
    if areaPoints == "" {
        pts = append(pts, Point{0, 0})
        pts = append(pts, Point{0, 540})
        pts = append(pts, Point{960, 540})
        pts = append(pts, Point{960, 0})
    } else {
        err := json.Unmarshal([]byte(areaPoints), &pts)
        if err != nil {
            fmt.Println("json.Unmarshal错误", err)
            panic("序列化坐标异常,程序退出")
        }
    }
    return pts
}
 
// 以摄像机id查出跟其相关的所有任务下的所有规则组
func GetRuleGroup(cameraId string) []*protomsg.TaskGroupArgs {
    // 查询数据库
    // 第一步查出跟这个摄像机相关的group_id(大规则)
    //var api dbapi.CameraTaskArgsApi
    //
    ////fmt.Println("所有规则:", all)
    //var taskArgs []*protomsg.TaskGroupArgs
    //for _, camArg := range all {
    //    if camArg.CameraId == cameraId {
    //        taskArgs = camArg.TaskArgs
    //    }
    //}
    all := cache.GetCameraTaskRulesByCameraId(cameraId)
    return all
}
 
// 联动任务的处理
func linkTask(args *SdkDatas, groupRule *protomsg.GroupRule, taskId string) {
    // new一个定时器,如果以此groupId为标志的定时器不存在的话
    var flag bool = true
    var timeEle = TimeElement{N: 3, InitN: 3, GroupId: groupRule.GroupId}
    for k, timeEle1 := range TimeEleList {
        if k == groupRule.GroupId {
            flag = false // 已经有了这个定时器就置为false 不再新增
            timeEle = *timeEle1
        }
    }
    if flag { // 如果还没有这个定时器元素就新增一个
        timeEle := TimeElement{N: 3, InitN: 3, GroupId: groupRule.GroupId} // 扔进去一个定时器元素
        //TimeEleList = make(map[string]timeElement)
        TimeEleList[groupRule.GroupId] = &timeEle // 定时器元素以规则组id为键
        fmt.Println("创建了计数器并且计数器集合为:", TimeEleList)
        // 得出这组完整规则里涉及到几个摄像机,决定着数组里有几个结构体,去重添加方式
        for j := 0; j < len(groupRule.Rules); j++ {
            var flag1 bool = true
            for _, ruleRes := range TimeEleList[groupRule.GroupId].RuleResults {
                if groupRule.Rules[j].CameraId == ruleRes.CameraId {
                    flag1 = false
                }
            }
            if flag1 {
                TimeEleList[groupRule.GroupId].RuleResults = append(TimeEleList[groupRule.GroupId].RuleResults, &RuleResult{groupRule.Rules[j].CameraId, groupRule.Rules[j].Sort, "", groupRule.Rules[j].RuleWithPre})
            }
        }
    }
    // 往数组里赋值
    isOk := RunRule(args, groupRule, taskId)
    if isOk {
        fmt.Println("这帧图像在任务下的一整条规则下(联动任务下就是跟本摄像机像相关的小规则)的判断结果为true")
        // 根据cameraId去更新或者插入结果,然后判断是否数组是否可以得出报警的结论
        // 往联动任务的结果数组里放值或更新
        for _, va := range timeEle.RuleResults {
            if va.CameraId == args.CameraId {
                va.Result = strconv.FormatBool(isOk)
            }
        }
        // 判断结果数组是否完满(即被赋值完毕)可得出报警结果
        var isPerfect = true
        for _, va := range timeEle.RuleResults {
            if va.Result == "" && va.RuleWithPre != "||" {
                isPerfect = false
            }
        }
        if isPerfect {
            // 将数组按sort排序
            sort.Sort(SubList(timeEle.RuleResults))
            // 排序后取各自的结果和连接符拼出规则表达式得出结果
            completeFormula := ""
            for _, va := range timeEle.RuleResults {
                completeFormula = completeFormula + va.RuleWithPre + "" + va.Result
            }
            if completeFormula != "" {
                expression, _ := govaluate.NewEvaluableExpression(completeFormula)
                result, _ := expression.Evaluate(nil) // 得到数学公式的结果
                if result.(bool) {
                    sdkName := ""
                    for j := 0; j < len(groupRule.Rules); j++ {
                        for _, sdkData := range args.Sdkdata {
                            if groupRule.Rules[j].SdkId == sdkData.SdkId && sdkData.IsYolo { // 只记录yolo符合规则的sdk
                                sdkName = sdkName + "," + sdkData.SdkName
                            }
                        }
                    }
                    // 过完规则后打个标签,告诉调用者本帧数据针对哪个任务哪组规则报警了
                    args.RuleResult["yolo"] = append(args.RuleResult["yolo"].([]Result), Result{taskId, sdkName, groupRule.GroupId, groupRule.AlarmLevel, groupRule.GroupText})
                }
            }
        } else {
            fmt.Println("数组不圆满不打标签")
        }
    } else { // 没有报警,
        fmt.Println("这帧图像在任务下的一整条规则下(联动任务下就是跟本摄像机像相关的小规则)的判断结果为false")
        // 所以也要去结果数组里放值或更新
        for _, va := range timeEle.RuleResults {
            if args.CameraId != "" && va.CameraId == args.CameraId { // arg.CameraId 随便找一个数据
                va.Result = strconv.FormatBool(isOk)
            }
        }
        // 因为本帧数据不符合规则,所以也不用统计结果数组里的东西
    }
}
 
// 过滤规则先筛选出符合条件的目标数量
func filterRule(rule *protomsg.Rule, am *AreaMap) LittleRuleResult {
    if rule.SdkId == "812b674b-2375-4589-919a-5c1c3278a97e" || rule.SdkId == "812b674b-2375-4589-919a-5c1c3278a971" {
        // 处理的是人脸算法 如果这条规则配置的是人脸算法,过滤完条件之后直接得出结果,因为肯定没有数量条件,自己拼接
 
        if rule.SdkId == am.sdkId && rule.PolygonId == am.areaId { // 算法和区域都得对的上
 
            if rule.SdkId == "812b674b-2375-4589-919a-5c1c3278a971" {
                // 只需要过滤阈值,过滤完后数组长度大于0即为报警,但要考虑如何对每一张都报警呢
                argValue, err := strconv.ParseFloat(rule.SdkArgValue, 64)
                if err != nil {
                    log.Println("规则配置的阈值非法")
                    return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + "false", rule.Sort}
                }
                flag := "false"
                for _, obj := range am.filterData {
                    for index := 0; index < len(obj.Liker); {
                        // 将达不到阈值的相似者从相似者数组中删除
                        if float64(obj.Liker[index].CompareScore) < argValue {
                            // Go 语言中切片删除元素的本质是:以被删除元素为分界点,将前后两个部分的内存重新连接起来。不用怀疑,数组删除元素就这么坑爹
                            obj.Liker = append(obj.Liker[:index], obj.Liker[index+1:]...)
                        } else {
                            index++
                        }
                    }
                }
                // 把没有相似者的人脸从filterData中删除
                for index := 0; index < len(am.filterData); {
                    // 将达不到阈值的相似者从相似者数组中删除
                    if len(am.filterData[index].Liker) == 0 {
                        // Go 语言中切片删除元素的本质是:以被删除元素为分界点,将前后两个部分的内存重新连接起来。不用怀疑,数组删除元素就这么坑爹
                        am.filterData = append(am.filterData[:index], am.filterData[index+1:]...)
                    } else {
                        index++
                    }
                }
                if len(am.filterData) > 0 {
                    flag = "true"
                }
                return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + flag, rule.Sort}
            }
            if rule.SdkId == "812b674b-2375-4589-919a-5c1c3278a97e" { // 人脸检测
                log.Println("------------------------------------------------------------------------------------------------------------------------------------终于等到你")
                if rule.Operator == "==" || rule.Operator == ">=" || rule.Operator == "<=" || rule.Operator == "<" || rule.Operator == ">" || rule.Operator == "!=" {
                    // 如果是不规矩的连接符统统返回false 规则也只能判断人脸的相似度,所以不存在别的连接符
                    if rule.SdkArgAlias == "score" || rule.SdkArgAlias == "proportion" || rule.SdkArgAlias == "size" { // 判断的是相似值,占比,尺寸等过滤条件,如果再有,还可以再加
                        var args []Arg
                        if rule.RuleWithPre == "&&" {
                            args = am.filterData
                        } else {
                            args = am.args
                        }
                        // 先清空过滤后的数据,再往里塞本次过滤后的数据
                        am.filterData = am.filterData[0:0]
                        for _, arg := range args {
                            var formula string
                            if rule.SdkArgAlias == "score" {
                                formula = strconv.FormatFloat(arg.Score, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                            } else if rule.SdkArgAlias == "proportion" {
                                formula = strconv.FormatFloat(arg.Proportion, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                            } else {
                                formula = strconv.FormatFloat(arg.Size, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                            }
                            expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
                            result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
                            if result.(bool) {
                                am.filterData = append(am.filterData, arg) // 得到符合条件的过滤数据
                            }
                        }
                        am.targetNum = len(am.filterData) // 把符合条件的目标数量更新到targetNum字段
                    }
                    if am.targetNum > 0 {
                        log.Println("!!!!!!!!!人脸检测成功")
                        return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + "true", rule.Sort}
                    } else {
                        return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + "false", rule.Sort}
                    }
                } else {
                    return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + "false", rule.Sort}
                }
            } else {
                return LittleRuleResult{}
            }
        } else {
            return LittleRuleResult{}
        }
    } else {
        // 处理的都是yolo数据
        if rule.SdkId == am.sdkId && rule.PolygonId == am.areaId { // 首先这条规则得是这个算法的规则,其次规则所对应的区域id要跟区域数据的id对的上
            if rule.SdkArgAlias == "score" || rule.SdkArgAlias == "proportion" || rule.SdkArgAlias == "size" { // 判断的是相似值,占比,尺寸等过滤条件,如果再有,还可以再加
                log.Println("----------当前条件规则:---------",rule)
                var args []Arg
                if rule.RuleWithPre == "&&" {
                    args = am.filterData
                } else {
                    args = am.args
                }
                // 先清空过滤后的数据,再往里塞本次过滤后的数据
                am.filterData = am.filterData[0:0]
                for _, arg := range args {
                    var formula string
                    if rule.SdkArgAlias == "score" {
                        formula = strconv.FormatFloat(arg.Score, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                        log.Println("当前相似度小公式:",formula)
                    } else if rule.SdkArgAlias == "proportion" {
                        formula = strconv.FormatFloat(arg.Proportion, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                        log.Println("当前占比小公式:",formula)
                    } else {
                        formula = strconv.FormatFloat(arg.Size, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                        log.Println("当前尺寸小公式:",formula)
                    }
                    expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
                    result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
                    if result.(bool) {
                        am.filterData = append(am.filterData, arg) // 得到符合条件的过滤数据
                    }
                }
                am.targetNum = len(am.filterData) // 把符合条件的目标数量更新到targetNum字段
                if am.targetNum > 0 {
                    return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + "true", rule.Sort}
                } else {
                    return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + "false", rule.Sort}
                }
 
            } else {
                return LittleRuleResult{}
            }
 
        } else {
            return LittleRuleResult{}
        }
    }
}
 
// 都过滤完条件之后看看是否要创建一个定时器元素 创建定时器的条件:是否有靠右行,个体静止等自带定时器含义的算法以及是否有持续时间
func duration(rule *protomsg.Rule, am *AreaMap) {
    if rule.SdkId == am.sdkId && rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上  配置的算法要对的上
        if rule.SdkArgAlias == "duration" { //
            // 先看看定时器元素队列中是否有这个摄像机这个区域的定时器,如果有就不能再次创建了
            var flag bool = true
            for k, _ := range TimeEleList {
                if k == am.taskId+" "+am.sdkId+" "+am.areaId {
                    flag = false // 有就置为false
                    fmt.Println("有这个定时器,不再创建了:")
                }
            }
 
            if flag {
                timeLength, _ := strconv.Atoi(rule.SdkArgValue)
                timeEle := TimeElement{N: timeLength, InitN: timeLength} // 扔进去一个定时器元素
                //TimeEleList = make(map[string]timeElement)
                TimeEleList[am.taskId+" "+am.sdkId+" "+am.areaId] = &timeEle // 定时器元素以摄像机id拼接区域id为键
                fmt.Println("创建了计数器并且计数器集合为:", TimeEleList)
            }
        }
    }
}
 
//// 冗余拼接一个目标数量大于0
//func splice1(am *AreaMap) string {
//    var num int = 0
//    for _, data := range am.filterData {
//        if data.isYolo {
//            num++
//        }
//    }
//    log.Println("看看区域内符合条件的目标数量:----------", num)
//    formula := strconv.Itoa(num) + " " + ">" + "0"
//    expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
//    result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
//    return strconv.FormatBool(result.(bool))
//}
 
// 给数据库的规则表达式代参 args: 一条子规则,区域数据
func transferParameters(rule *protomsg.Rule, am *AreaMap) LittleRuleResult {
    if rule.SdkId == am.sdkId && rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "targetNum" { // 如果参数是要区域内目标数量 即yolo 人脸不会有数量
            //fmt.Println("得出结果阶段", "比较的规则是:", rule)
            if rule.Operator == "" {
                return LittleRuleResult{am.sdkName, strconv.Itoa(am.targetNum) + "" + rule.RuleWithPre, rule.Sort} // 如果后面不跟操作符就直接返回数量  比如要跟下一个区域比较数量的就直接返回本区域的数量
            }
            //args := am.targetNum     targetNum 已成所有目标的总数量,这里只算yolo的
            var num int = 0
            for _, data := range am.filterData {
                if data.IsYolo {
                    num++
                }
            }
            formula := strconv.Itoa(num) + " " + rule.Operator + " " + rule.SdkArgValue
            expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
            result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
            return LittleRuleResult{am.sdkName, strconv.FormatBool(result.(bool)) + "" + rule.RuleWithPre, rule.Sort}
            // 加上关于算法的判断条件,不能只有关于规则的,有的算法本身就是一个规则,如个体静止,靠右行,所以,拿到当前子规则的sdkid来判断是否是那些特殊的规则
        } else if rule.SdkId == "IsStatic" { // 静止算法
            if am.isStatic {
                return LittleRuleResult{}
            } else {
                return LittleRuleResult{}
            }
        } else if rule.SdkId == "KeepRight" { // 靠右行算法
            if am.keepRight {
                return LittleRuleResult{}
            } else {
                return LittleRuleResult{}
            }
        }
    }
    return LittleRuleResult{}
}
func timeRuleResult(rule *protomsg.Rule, am *AreaMap) LittleRuleResult {
    if rule.PolygonId == am.areaId && rule.SdkId == am.sdkId{ // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "time_rule" { // 判断是否符合时间规则
            log.Println("----------当前时间规则:---------",rule)
            // 根据放值字段里存的时间规则的id去另一个表里查需要比对的时间段(比如当前时间是周三,应根据区域id查出其周三的几个布防时间段,数组)
            //fmt.Println("时间规则的测试")
            now := time.Now()
            index := getIndexOfWeek(now.Weekday().String())
            timeList := GetTimeById(rule.SdkArgValue, index)
            log.Println("当天的时间段集合:----------",timeList)
            //fmt.Println("从数据库中查出的时间规则:", timeList)
            // 判断图片数据的时间是否符合当前规则 在一个即为true,全不在为false
            flag := "false"
            for _, timeSlot := range timeList {
                if rule.Operator == "satisfy" || rule.Operator == "=="{ // 满足所选的时间规则
                    formula := "'" + timeSlot.Start + "'" + "<" + "'" + am.time + "'"
                    expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
                    result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
 
                    formula1 := "'" + timeSlot.End + "'" + ">" + "'" + am.time + "'"
                    expression1, _ := govaluate.NewEvaluableExpression(formula1) // 得到数学公式
                    result1, _ := expression1.Evaluate(nil)                      // 得到数学公式的结果
                    //fmt.Println("看看这两尊大神", result, result1)
                    if result.(bool) && result1.(bool) {
                        flag = "true"
                        break
                    }
                }
                if rule.Operator == "unsatisfy" || rule.Operator == "!="{ // 不满足所选的时间规则
                    formula := timeSlot.Start + "<" + am.time
                    expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
                    result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
 
                    formula1 := timeSlot.End + ">" + am.time
                    expression1, _ := govaluate.NewEvaluableExpression(formula1) // 得到数学公式
                    result1, _ := expression1.Evaluate(nil)                      // 得到数学公式的结果
                    if result.(bool) && result1.(bool) {
                        flag = "true"
                        break
                    }
                }
            }
            return LittleRuleResult{am.sdkName, rule.RuleWithPre + "" + flag, rule.Sort}
        }
    }
    return LittleRuleResult{}
}
 
// 根据传入的字符串得到其在一周内的索引 周一到周日分别对应1到7
func getIndexOfWeek(weekday string) int {
    var weekdays = [7]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
    for k, value := range weekdays {
        if value == weekday {
            return k + 1 // 因为数据库中存的是1-7代表的周一到周日
        }
    }
    return 0
}
 
type TimeRange struct {
    Start string `json:"start"`
    End   string `json:"end"`
}
type day struct {
    Day       int         `json:"day"`        // 标示当前星期几
    TimeRange []TimeRange `json:"time_range"` // 当天的几个时间段
}
 
// 取出某个时间规则的第几天的规则段集合
func GetTimeById(id string, index int) []TimeRange {
    //var cameraTimeRule protomsg.CameraTimerule
    //var api dbapi.CameraApi
    //_, rules := api.FindAllTimeRules()
    //for _, rule := range rules {
    //    if rule.Id == id {
    //        cameraTimeRule = rule
    //    }
    //}
    _,cameraTimeRule := cache.GetTimeRuleById(id)
    var timeRangeList []day
    json.Unmarshal([]byte(cameraTimeRule.TimeRule), &timeRangeList)
    for _, timerange := range timeRangeList {
        if timerange.Day == index {
            //log.Println("取到的时间规则:", timerange.TimeRange)
            return timerange.TimeRange
        }
    }
    return nil
}