panlei
2019-06-20 04415088e884dd59fe917572a997c15778267fac
....
3个文件已添加
783 ■■■■■ 已修改文件
ruleserver/geoPolygon.go 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruleserver/ruleToformula.go 565 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruleserver/timeTicker.go 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruleserver/geoPolygon.go
New file
@@ -0,0 +1,126 @@
package ruleserver
import (
    "math"
)
func min(num1 float64, num2 float64) float64 {
    if num1 < num2 {
        return num1
    }
    return num2
}
func max(num1 float64, num2 float64) float64 {
    if num1 < num2 {
        return num2
    }
    return num1
}
//Point 坐标点
type Point struct {
    X float64 `json:"x"`
    Y float64 `json:"y"`
}
//Rect 检测目标
type Rect struct {
    X      float64
    Y      float64
    Width  float64
    Height float64
}
//Pointfloat 坐标点
type Pointfloat struct {
    X float64 `json:"x"`
    Y float64 `json:"y"`
}
//PintIsInPolygon 判断点是否在多边形内部
//point为要判断的坐标点
//polygon是多边形各点数组
func pintIsInPolygon(point Pointfloat, polygon []Point, widthScale float64, heightScale float64) bool {
    var nCross int = 0
    for i := 0; i < len(polygon); i++ {
        p1 := polygon[i]
        p2 := polygon[(i+1)%len(polygon)]
        // 根据摄像机所拍图像与前端画布的比例将区域放大
        p1.X = p1.X * widthScale
        p1.Y = p1.Y * heightScale
        p2.X = p2.X * widthScale
        p2.Y = p2.Y * heightScale
        if p1.Y == p2.Y {
            continue
        }
        if point.Y < min(p1.Y, p2.Y) {
            continue
        }
        if point.Y > max(p1.Y, p2.Y) {
            continue
        }
        X := (point.Y-p1.Y)*(p2.X-p1.X)/(p2.Y-p1.Y) + p1.X
        if X > point.X {
            nCross++
        }
    }
    return nCross%2 != 0
}
//GetLocation 将一个给定起始坐标,宽度长度的矩形区域均分为n方份并返回中心坐标(n为单边平分数值)和面积
func getLocation(rect Rect, n int) ([]Pointfloat, float64) {
    xArr := make([]float64, n) // 用切片不用数组,数组不能用变量定义长度
    yArr := make([]float64, n)
    pointArr := make([]Pointfloat, 0, n*n)
    for i := 0; i < n; i++ {
        xArr[i] = rect.X + (rect.Width/float64(2*n))*float64(2*i+1)
        yArr[i] = rect.Y + (rect.Height/float64(2*n))*float64(2*i+1)
    }
    for i := 0; i < n; i++ {
        for j := 0; j < n; j++ {
            point := Pointfloat{X: xArr[i], Y: yArr[j]}
            pointArr = append(pointArr, point)
        }
    }
    s := rect.Width * rect.Height
    return pointArr, s
}
//ComputePolygonArea 计算任意多边形面积
func computePolygonArea(polygon []Point) float64 {
    pointNum := len(polygon)
    var s float64 = 0
    if pointNum < 3 {
        s = 0
    }
    for i := 0; i < pointNum; i++ {
        s += polygon[i].X*polygon[(i+1)%pointNum].Y - polygon[i].Y*polygon[(i+1)%pointNum].X
    }
    area := math.Abs(s / 2)
    return area
}
//PgsInterPercent calculate percent of two polygon intersection  计算两个多边形的重叠占比
func PgsInterPercent(pgpts []Point, box Rect, widthScale float64, heightScale float64) (percent float64) {
    areapts, areaBox := getLocation(box, 10)
    var count = 0
    for _, pts := range areapts {
        if pintIsInPolygon(pts, pgpts, widthScale, heightScale) {
            count++
        }
    }
    perInterBox := float64(count) / float64(len(areapts)) // 重合面积占矩形的比例
    areaInter := perInterBox * areaBox
    areaPg := computePolygonArea(pgpts)
    perInterPg := areaInter / areaPg // 重合面积占多边形区域的比例
    // 哪个占的比例大按哪个计算
    if perInterBox > perInterPg {
        return perInterBox
    }
    return perInterPg
}
ruleserver/ruleToformula.go
New file
@@ -0,0 +1,565 @@
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 // 区域内的目标的尺寸
}
// 每个区域内的图片数据集合
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 // 相似度得分
}
// 从通道中获取的sdk输出的图像数据(目前主要是yolo算法的数据)
type ArgsFromSdk struct {
    Photo       []PhotoMap
    CameraId    string
    TaskId      string
    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
}
// 包含N条规则元素的一整条规则
type CompleteRule struct {
    rule string
}
// 摄像机区域 跟数据库映射的
// type CameraPolygon struct {
//     Id            string `json:"id"`
//     CameraId      string `json:"camera_id"`
//     Name          string `json:"name"`
//     Polygon       string `json:"polygon"` // 坐标点区域
//     TriggerLine   string `json:"trigger_line"`
//     DirectionLine string `json:"direction_line"`
// }
// 根据摄像机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 (arg *ArgsFromSdk) MainJudge() {
    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
    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 aml.areaMapList[0].cameraId != "" && va.CameraId == aml.areaMapList[0].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 aml.areaMapList[0].cameraId != "" && va.CameraId == aml.areaMapList[0].cameraId { // aml.areaMapList[0].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 {
        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
            }
        }
        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: taskId, RuleGroupId: groupRule.GroupId})
                return true
            } else {
                return false
            }
        }
    } else {
        return false
    }
}
// 对单帧图像的判断 是舍弃(或者说对于某些需求可以放ES数据库一份)还是返回
func judge(aml *AreaMapList, arg *ArgsFromSdk) {
    // 得到属于该摄像机的若干组任务的完整规则(跟每一条完整规则比较之后得出本张图像对于某个规则是否报警的结果。放进map,比如本帧图像的id,所碰撞成功的规则id)
    taskRuleList := GetRuleGroup(aml.areaMapList[0].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字段
            //fmt.Println("筛选完后的内容:", am)
        }
    }
}
// 都过滤完条件之后看看是否要创建一个定时器元素 创建定时器的条件:是否有靠右行,个体静止等自带定时器含义的算法以及是否有持续时间
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)
            }
        }
    }
}
// 给数据库的规则表达式代参 args: 一条子规则,区域数据
func transferParameters(rule *protomsg.Rule, am *AreaMap) string {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "targetNum" { // 如果参数是要区域内目标数量
            //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 == "个体静止" { // 暂时用汉字代替啦,晚些替换成正式的id
            if am.isStatic {
                return "true"
            } else {
                return "false"
            }
        } else if rule.SdkId == "靠右行" { // 暂时用汉字代替啦,晚些替换成正式的id
            if am.keepRight {
                return "true"
            } else {
                return "false"
            }
        }
    }
    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
}
ruleserver/timeTicker.go
New file
@@ -0,0 +1,92 @@
package ruleserver
import (
    "fmt"
    "os"
    "time"
)
var stopChan = make(chan bool)
// 计数器map 独立任务的键是任务id拼接区域id 联动任务的键是groupid(能不能都用groupId?)
var TimeEleList = make(map[string]*TimeElement)
// 定时器元素
type TimeElement struct {
    N           int     // 按时间依次递减的当前值
    InitN       int     // 赋值后就不变的初始值
    Data        AreaMap //
    GroupId     string  // 联动规则需要记录下此时的规则组id
    RuleResults []*RuleResult
}
type RuleResult struct { // 每个摄像机一个结构体
    CameraId    string // 摄像机id
    Sort        int32  // 摄像机在规则组中序号
    Result      string // 摄像机过滤数据得出的结果
    RuleWithPre string // 摄像机之间的连接符
}
func TimeTicker() {
    fmt.Println("定时器执行了")
    ticker := time.NewTicker(1 * time.Second)
    go func(ticker *time.Ticker) {
        defer ticker.Stop()
        for {
            select {
            case <-ticker.C:
                fmt.Println("定时器执行单元")
                // 每秒钟计数器池子里所有的计数器元素都减一,减到0的是该报警的
                for _, timeEle := range TimeEleList {
                    if timeEle.N > 0 {
                        timeEle.N = timeEle.N - 1
                    }
                }
            case stop := <-stopChan:
                if stop {
                    fmt.Println("定时器结束")
                    os.Exit(0)
                }
            }
        }
    }(ticker)
}
func StopTimeTicker() {
    stopChan <- true
    TimeTicker()
}
// 定时器单元  废弃版本
// func TimeTicker() chan bool {
//     fmt.Println("执行了timeTicker")
//     ticker := time.NewTicker(1 * time.Second)
//     stopChan := make(chan bool)
//     go func(ticker *time.Ticker) {
//         defer ticker.Stop()
//         for {
//             select {
//             case <-ticker.C:
//                 //fmt.Println("执行单元", "计数器集合2", TimeEleList)
//                 for k, timeEle := range TimeEleList {
//                     timeEle.n = timeEle.n - 1
//                     //fmt.Println("遍历的数值", TimeEleList)
//                     if timeEle.n == 0 {
//                         // do something alarm
//                         alarm(k, timeEle)
//                     }
//                 }
//             case stop := <-stopChan:
//                 if stop {
//                     fmt.Println("定时器结束")
//                     return
//                 }
//             }
//         }
//     }(ticker)
//     return stopChan
// }
type SubList []*RuleResult
func (p SubList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
func (p SubList) Len() int           { return len(p) }
func (p SubList) Less(i, j int) bool { return p[i].Sort < p[j].Sort }