--
panlei
2019-06-25 6b5f372e6077e1b0880b6ba389375641c0a1a15a
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
package ruleserver
 
import (
    "encoding/json"
    "fmt"
    "sort"
    "strconv"
    "strings"
    "time"
 
    "basic.com/dbapi.git"
    "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 {
    id         string
    score      float64 // 区域内的目标的相似度
    proportion float64 // 区域内的目标的占比
    size       float64 // 区域内的目标的尺寸
    liker      []LikePerson
}
 
type LikePerson struct {
    Id    string  // 与之相似的底库人员的id
    Score float64 // 与底库人员的相似值
}
 
// 每个区域内的图片数据集合
type AreaMap struct {
    cameraId      string
    areaId        string
    groupId       string
    taskId        string
    sdkIds        []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 // 相似度得分(有多大程度像一个目标。人脸,人体或车等等)
    Liker []LikePerson // 如果是人脸的话尤其是比对,应存下他跟底库的人员的相似情况 yolo的话给nil就行
}
 
// 从通道中获取的sdk输出的图像数据(目前主要是yolo算法的数据)
type ArgsFromSdk struct {
    CameraId    string
    TaskId      string
    Photo       []PhotoMap // yolo算法结构,也可以存人脸的数据,毕竟人脸中能用规则来测的还是那些参数
    KeepRight   bool       // 是否靠右行 算法判断的与上一帧图像的比较结果
    IsStatic    bool       // 是否静止
    ImageWidth  int        // 摄像机拍摄的图像宽 像素
    ImageHeight int        // 摄像机拍摄的图像高 像素
    RuleResult  []Result   // 过完规则后打的标签
}
 
// 将传递过来的参数转化为
//protomsg.SdkMessage.TaskLabel.SdkmsgWithTask.sdkdata
 
type ResultMsg struct {
    protomsg.SdkMessage
    RuleResult []Result // 过完规则后打的标签
}
 
// 过规则库打上的标签
type Result struct {
    TaskId      string // 任务id
    RuleGroupId string // 规则组id
    AlarmLevel  int32  // 报警等级
    RuleText    string // 文字版规则组
}
 
// 包含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 {
            // 需要根据比例把前台画的区域的坐标转化为相应摄像机拍摄的图像的大小   x坐标分别*image.width/页面区域宽  y坐标分别*image.height/页面区域高
            // 前台宽高固定
            cameraPolygons = append(cameraPolygons, item)
        }
    }
    //log.Println("根据摄像机id查到的区域", cameraPolygons, "--区域数量为:", len(cameraPolygons))
    return cameraPolygons
}
 
// 规则主函数入口
func MainJudge(arg *ArgsFromSdk) {
    cameraPolygons := GetPolygons(arg.CameraId)
    list := AreaMapList{}
    for _, polygon := range cameraPolygons {
        areaMap := AreaMap{cameraId: arg.CameraId, areaId: polygon.Id, areaJson: polygon.Polygon, triggerLine: polygon.TriggerLine, directionLine: polygon.DirectionLine}
        // 为每个摄像机区域填充数据
        areaMap.CountAreaObjs(arg)
        list.areaMapList = append(list.areaMapList, areaMap)
    }
    //fmt.Println("为每个摄像机区域填充数据后的内容", list.areaMapList)
    // 将此帧数据按摄像机区域分类打包后判断是否报警
    judge(&list, arg)
}
 
// 计算区域内的目标数量以及将相似度、占比、尺寸等打包
func (a *AreaMap) CountAreaObjs(arg *ArgsFromSdk) {
 
    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 {
        if threshold <= obj.Score &&
            size <= float64(obj.Rects.Width*obj.Rects.Height) &&
            intersectionper <= PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale) {
            // 这步要备齐表达式里所需要的所有参数
            a.targetNum++
            arg := Arg{score: obj.Score, proportion: PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale), size: float64(obj.Rects.Width * obj.Rects.Height)}
            a.args = append(a.args, arg)
            a.filterData = append(a.filterData, arg)
        }
    }
    a.time = time.Unix(time.Now().Unix(), 0).String()[11:16]
    a.keepRight = arg.KeepRight
    a.isStatic = arg.IsStatic
}
 
// 将字符串格式的坐标序列化为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
    all := api.FindAll()
    //fmt.Println("所有规则:", all)
    var taskArgs []*protomsg.TaskGroupArgs
    for _, taskArg := range all {
        if taskArg.CameraId == cameraId {
            taskArgs = taskArg.TaskArgs
        }
    }
    return taskArgs
}
 
// 联动任务的处理
func linkTask(aml *AreaMapList, arg *ArgsFromSdk, 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 = false
            for _, ruleRes := range TimeEleList[groupRule.GroupId].RuleResults {
                if groupRule.Rules[j].CameraId == ruleRes.CameraId {
                    flag1 = true
                }
            }
            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 := singleTask(aml, arg, groupRule, taskId)
    if isOk {
        fmt.Println("这帧图像在任务下的一整条规则下(联动任务下就是跟本摄像机像相关的小规则)的判断结果为true")
        // 根据cameraId去更新或者插入结果,然后判断是否数组是否可以得出报警的结论
        // 往联动任务的结果数组里放值或更新
        for _, va := range timeEle.RuleResults {
            if arg.CameraId != "" && va.CameraId == arg.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) {
                    // 过完规则后打个标签,告诉调用者本帧数据针对哪个任务哪组规则报警了
                    arg.RuleResult = append(arg.RuleResult, Result{TaskId: taskId, RuleGroupId: groupRule.GroupId})
                }
            }
        } else {
            fmt.Println("数组不圆满不打标签")
        }
    } else { // 没有报警,
        fmt.Println("这帧图像在任务下的一整条规则下(联动任务下就是跟本摄像机像相关的小规则)的判断结果为false")
        // 所以也要去结果数组里放值或更新
        for _, va := range timeEle.RuleResults {
            if arg.CameraId != "" && va.CameraId == arg.CameraId { // arg.CameraId 随便找一个数据
                va.Result = strconv.FormatBool(isOk)
            }
        }
        // 因为本帧数据不符合规则,所以也不用统计结果数组里的东西
    }
}
 
// 独立任务
func singleTask(aml *AreaMapList, arg *ArgsFromSdk, groupRule *protomsg.GroupRule, taskId string) bool {
    var completeFormula string = ""
    for _, areaMap := range aml.areaMapList {
        fmt.Println("当前规则组为---------:",groupRule)
        for j := 0; j < len(groupRule.Rules); j++ {
            // 先过完条件数据
            filterRule(groupRule.Rules[j], &areaMap)
        }
        for j := 0; j < len(groupRule.Rules); j++ {
            // 再过其他数据 这步直接得到结果(真或假) 过目标数量
            flag := transferParameters(groupRule.Rules[j], &areaMap)
            if flag != "" {
                fmt.Println("得出的结果", flag)
                completeFormula = completeFormula + groupRule.Rules[j].RuleWithPre + "" + flag
            }
        }
        if completeFormula == "" {
            flag := splice1(&areaMap)
            if flag != "" {
                fmt.Println("强行拼凑一个人数是否大于0的结果", flag)
                completeFormula = flag
            }
        }
        for j := 0; j < len(groupRule.Rules); j++ {
            // 这步过的是时间规则(时间段等)
            flag := timeRuleResult(groupRule.Rules[j], &areaMap)
            if flag != "" {
                fmt.Println("时间规则的结果", flag)
                completeFormula = completeFormula + groupRule.Rules[j].RuleWithPre + "" + flag
            }
        }
 
        for j := 0; j < len(groupRule.Rules); j++ {
            // 最后过持续时间等时间维度的条件
            duration(groupRule.Rules[j], &areaMap)
        }
 
    }
    fmt.Println("拼出的数学公式为:==== ", completeFormula)
    if completeFormula != "" {
        expression, _ := govaluate.NewEvaluableExpression(completeFormula)
        result, _ := expression.Evaluate(nil) // 得到数学公式的结果
        fmt.Println("这帧图像在任务下的除了持续时间外的一整条规则下的判断结果", result)
        // 由于天然或的关系,满足一个就该报警,即该帧数据对于某个任务的某个规则组应该报警
        if !result.(bool) { // 如果不符合条件,应该重置定时器元素,等符合时再开启,把key中包含任务id的timeEle都重置
            for k, timeEle := range TimeEleList {
                if strings.Index(k, taskId) != -1 {
                    timeEle.N = timeEle.InitN // 重置定时器
                }
            }
            return false
        } else {
            // 去看定时器此时是否走到0,走到0的话返回成功报警
            var flag bool = true
            for k, timeEle := range TimeEleList {
                if strings.Index(k, taskId) != -1 {
                    if timeEle.N != 0 { // 跟这个任务有关的定时器要全部等于0
                        flag = false
                    }
                }
            }
            if flag {
                fmt.Println("定时器报警了")
                // 过完规则后打个标签,告诉调用者本帧数据针对哪个任务哪组规则报警了
                arg.RuleResult = append(arg.RuleResult, Result{taskId, groupRule.GroupId, groupRule.AlarmLevel, groupRule.GroupText})
                return true
            } else {
                return false
            }
        }
    } else {
        return false
    }
}
 
// 对单帧图像的判断 是舍弃(或者说对于某些需求可以放ES数据库一份)还是返回
func judge(aml *AreaMapList, arg *ArgsFromSdk) {
    // 得到属于该摄像机的若干组任务的完整规则(跟每一条完整规则比较之后得出本张图像对于某个规则是否报警的结果。放进map,比如本帧图像的id,所碰撞成功的规则id)
    taskRuleList := GetRuleGroup(arg.CameraId)
    if len(taskRuleList) > 0 {
        for _, taskRule := range taskRuleList {
            ruleList := taskRule.GroupRules // 获取的是task下面的任务组
            taskId := taskRule.TaskId
            for i := 0; i < len(ruleList); i++ {
                temp := ruleList[i].Rules // temp为一组完整规则 在此需要判断规则是否是联动规则
                if len(temp) > 0 {
                    if strings.Contains(ruleList[i].GroupId, "link") {
                        // groupId中含有link则为联动任务
                        linkTask(aml, arg, ruleList[i], taskId)
                    } else {
                        // 独立任务的处理
                        singleTask(aml, arg, ruleList[i], taskId)
                    }
                }
            }
        }
    }
}
 
// 过滤规则先筛选出符合条件的目标数量
func filterRule(rule *protomsg.Rule, am *AreaMap) {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "score" || rule.SdkArgAlias == "proportion" || rule.SdkArgAlias == "size" { // 判断的是相似值,占比,尺寸等过滤条件,如果再有,还可以再加
            //fmt.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 // 得到字符串公式
                } 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字段
        }
    }
}
 
// 都过滤完条件之后看看是否要创建一个定时器元素 创建定时器的条件:是否有靠右行,个体静止等自带定时器含义的算法以及是否有持续时间
func duration(rule *protomsg.Rule, am *AreaMap) {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "duration" { //
            // 先看看定时器元素队列中是否有这个摄像机这个区域的定时器,如果有就不能再次创建了
            var flag bool = true
            for k, _ := range TimeEleList {
                if k == am.taskId+" "+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.areaId] = &timeEle // 定时器元素以摄像机id拼接区域id为键
                fmt.Println("创建了计数器并且计数器集合为:", TimeEleList)
            }
        }
    }
}
 
// 冗余拼接
func splice1(am *AreaMap) string {
    args := am.targetNum
    formula := strconv.Itoa(args) + " " + ">" + "0"
    expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
    result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
    return strconv.FormatBool(result.(bool))
}
 
// 给数据库的规则表达式代参 args: 一条子规则,区域数据
func transferParameters(rule *protomsg.Rule, am *AreaMap) string {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "targetNum" { // 如果参数是要区域内目标数量 即yolo
            //fmt.Println("得出结果阶段", "比较的规则是:", rule)
            if rule.Operator == "" {
                return strconv.Itoa(am.targetNum) // 如果后面不跟操作符就直接返回数量  比如要跟下一个区域比较数量的就直接返回本区域的数量
            }
            args := am.targetNum
            formula := strconv.Itoa(args) + " " + rule.Operator + " " + rule.SdkArgValue
            expression, _ := govaluate.NewEvaluableExpression(formula) // 得到数学公式
            result, _ := expression.Evaluate(nil)                      // 得到数学公式的结果
            return strconv.FormatBool(result.(bool))
            // 加上关于算法的判断条件,不能只有关于规则的,有的算法本身就是一个规则,如个体静止,靠右行,所以,拿到当前子规则的sdkid来判断是否是那些特殊的规则
        } else if rule.SdkId == "IsStatic" { // 静止算法
            if am.isStatic {
                return "true"
            } else {
                return "false"
            }
        } else if rule.SdkId == "KeepRight" { // 靠右行算法
            if am.keepRight {
                return "true"
            } else {
                return "false"
            }
        } else if rule.SdkId == "FaceDetect" { // 人脸检测
            if rule.Operator == "==" || rule.Operator == ">=" || rule.Operator == "<=" || rule.Operator == "<" || rule.Operator == ">" || rule.Operator == "!=" {
                // 如果是不规矩的连接符统统返回false 规则也只能判断人脸的相似度,所以不存在别的连接符
 
                return "false"
            } else {
                return "false"
            }
        } else if rule.SdkId == "FaceCompare"{
 
        }
 
    }
    return ""
}
func timeRuleResult(rule *protomsg.Rule, am *AreaMap) string {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "time" { // 判断是否符合时间规则
            // 根据放值字段里存的时间规则的id去另一个表里查需要比对的时间段(比如当前时间是周三,应根据区域id查出其周三的几个布防时间段,数组)
            //fmt.Println("时间规则的测试")
            now := time.Now()
            index := getIndexOfWeek(now.Weekday().String())
            timeList := GetTimeById(rule.SdkArgValue, index)
            //fmt.Println("从数据库中查出的时间规则:", timeList)
            // 判断图片数据的时间是否符合当前规则 在一个即为true,全不在为false
            flag := "false"
            for _, timeSlot := range timeList {
                if rule.Operator == "satisfy" { // 满足所选的时间规则
                    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" { // 不满足所选的时间规则
                    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 flag
        }
    }
    return ""
}
 
// 根据传入的字符串得到其在一周内的索引 周一到周日分别对应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
        }
    }
    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
}