panlei
2019-06-26 a3a788801fcd2efb16d58e91f393bad8d7730b96
加上人脸,过完规则的标签区分人脸和yolo
5个文件已修改
349 ■■■■■ 已修改文件
go.mod 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
go.sum 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
insertdata/insertDataToEs.go 144 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.go 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruleserver/ruleToformula.go 137 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
go.mod
@@ -4,7 +4,7 @@
require (
    basic.com/dbapi.git v0.0.0-20190622030047-3ea90a522ec1
    basic.com/pubsub/protomsg.git v0.0.0-20190622044013-0d3ee5a9c502
    basic.com/pubsub/protomsg.git v0.0.0-20190625090102-59334c91e550
    basic.com/valib/deliver.git v0.0.0-20190531095353-25d8c3b20051
    github.com/Microsoft/go-winio v0.4.12 // indirect
    github.com/ajg/form v1.5.1 // indirect
@@ -17,7 +17,7 @@
    github.com/tmthrgd/go-sem v0.0.0-20160607101025-0214dbf53877 // indirect
    github.com/tmthrgd/go-shm v0.0.0-20170117044846-90afcfcd5ee9 // indirect
    github.com/tmthrgd/shm-go v0.0.0-20170130075737-7207ca97b290 // indirect
    gocv.io/x/gocv v0.20.0 // indirect
    gocv.io/x/gocv v0.20.0
    golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed // indirect
    nanomsg.org/go-mangos v1.4.0 // indirect
)
go.sum
@@ -1,9 +1,7 @@
basic.com/dbapi.git v0.0.0-20190622030047-3ea90a522ec1 h1:MAAgMzO1rd9+gYYksrtOWIB4dqQZvZEjwaZImygpSgk=
basic.com/dbapi.git v0.0.0-20190622030047-3ea90a522ec1/go.mod h1:eDXPnxaz6jZPDvBSk7ya7oSASWPCuUEgRTJCjsfKt/Q=
basic.com/pubsub/protomsg.git v0.0.0-20190622023307-97ef3bf336ef h1:gUV9FEa23v+2AY4Rvrwh6omHBlV+qyae58GTU8NAv3Y=
basic.com/pubsub/protomsg.git v0.0.0-20190622023307-97ef3bf336ef/go.mod h1:un5NV5VWQoblVLZfx1Rt5vyLgwR0jI92d3VJhfrJhWU=
basic.com/pubsub/protomsg.git v0.0.0-20190622044013-0d3ee5a9c502 h1:H+//LYF4+YwVD2A9GUP0ETL8p5RANgtDHpOgu/421sE=
basic.com/pubsub/protomsg.git v0.0.0-20190622044013-0d3ee5a9c502/go.mod h1:un5NV5VWQoblVLZfx1Rt5vyLgwR0jI92d3VJhfrJhWU=
basic.com/pubsub/protomsg.git v0.0.0-20190625090102-59334c91e550 h1:sCzugx8u2QCXTirZYM7Rkh/kLn8HYvJH3ly415l2meY=
basic.com/pubsub/protomsg.git v0.0.0-20190625090102-59334c91e550/go.mod h1:un5NV5VWQoblVLZfx1Rt5vyLgwR0jI92d3VJhfrJhWU=
basic.com/valib/deliver.git v0.0.0-20190531095353-25d8c3b20051 h1:9flC2o3kasaM2Y6I+mY+mxmve/pyAY/UzGQZLT3lFHM=
basic.com/valib/deliver.git v0.0.0-20190531095353-25d8c3b20051/go.mod h1:bkYiTUGzckyNOjAgn9rB/DOjFzwoSHJlruuWQ6hu6IY=
code.cloudfoundry.org/bytefmt v0.0.0-20180906201452-2aa6f33b730c/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
insertdata/insertDataToEs.go
@@ -93,76 +93,84 @@
// 往ES插数据
func InsertToEs(msg ruleserver.ResultMsg) {
    log.Println("往ES插数据")
    for _, sdkinfo := range msg.Tasklab.Sdkinfos {
        if sdkinfo.Sdktype == "FaceDetect" {
            if len(sdkinfo.Sdkdata) > 1 {
                faceParam := protomsg.ParamFacePos{}
                err1 := proto.Unmarshal(sdkinfo.Sdkdata, &faceParam)
                if err1 != nil {
                    log.Println("解析FACE sdk有误", err1)
                    continue
                }
                for _, face := range faceParam.Faces {
                    pervideo := PerVideoPicture{
                        uuid.NewV4().String(),
                        msg.Cid,
                        msg.Caddr,
                        time.Now().Format("2006-01-02 15:04:05"),
                        "",
                        msg.Tasklab.Taskid,
                        msg.Tasklab.Taskname,
                        sdkinfo.SdkName,
                        "",
                        "",
                        face.Result.Gender,
                        face.Result.Age,
                        "",
                        face.Result.Race,
                        face.Result.Smile,
                        face.Result.Beauty,
                        "不是每个人脸算法都有",
                        "---",
                        "",
                        "",
                        "",
                        "",
                        "",
                        "",
                        "",
                        "",
                        0,
                        Base{
                            "是每个人脸算法都有吗",
                            "",
                            0,
                            "",
                            "",
                            "",
                            "",
                            "",
                            "",
                            "",
                            "",
                        },
                    }
                    requstbody, err := json.Marshal(pervideo)
                    if err != nil {
                        log.Println("json parse error ", err)
                        return
                    }
                    err = EsReq("POST", "http://192.168.1.182:9200/videopersons/perVideoPicture", requstbody)
                    if err != nil {
                        log.Println("es can not execute right.")
                    }
                }
            } else {
                continue
    // 直接从规则的标签数据里拿符合规则的人脸结果
    for _, result := range msg.RuleResult {
        if !result.IsYolo {
            for _,face1 := range result.Faces{
                println(face1)
            }
        }
    }
    for _, sdkinfo := range msg.Tasklab.Sdkinfos {
        //if sdkinfo.Sdktype == "FaceDetect" {
        //    if len(sdkinfo.Sdkdata) > 1 {
        //
        //        faceParam := protomsg.ParamFacePos{}
        //        err1 := proto.Unmarshal(sdkinfo.Sdkdata, &faceParam)
        //        if err1 != nil {
        //            log.Println("解析FACE sdk有误", err1)
        //            continue
        //        }
        //        for _, face := range faceParam.Faces {
        //            pervideo := PerVideoPicture{
        //                uuid.NewV4().String(),
        //                msg.Cid,
        //                msg.Caddr,
        //                time.Now().Format("2006-01-02 15:04:05"),
        //                "",
        //                msg.Tasklab.Taskid,
        //                msg.Tasklab.Taskname,
        //                sdkinfo.SdkName,
        //                "",
        //                "",
        //                face.Result.Gender,
        //                face.Result.Age,
        //                "",
        //                face.Result.Race,
        //                face.Result.Smile,
        //                face.Result.Beauty,
        //                "不是每个人脸算法都有",
        //                "---",
        //                "",
        //                "",
        //                "",
        //                "",
        //                "",
        //                "",
        //                "",
        //                "",
        //                0,
        //                Base{ // 只有人脸比对有这个信息,人脸检测并没有
        //                    "是每个人脸算法都有吗",
        //                    "",
        //                    0,
        //                    "",
        //                    "",
        //                    "",
        //                    "",
        //                    "",
        //                    "",
        //                    "",
        //                    "",
        //                },
        //            }
        //            requstbody, err := json.Marshal(pervideo)
        //
        //            if err != nil {
        //                log.Println("json parse error ", err)
        //                return
        //
        //            }
        //            err = EsReq("POST", "http://192.168.1.182:9200/videopersons/perVideoPicture", requstbody)
        //            if err != nil {
        //                log.Println("es can not execute right.")
        //            }
        //        }
        //
        //    } else {
        //        continue
        //    }
        //}
        if sdkinfo.Sdktype == "Yolo" {
            if len(sdkinfo.Sdkdata) > 1 {
main.go
@@ -82,45 +82,44 @@
    arg.KeepRight = false
    arg.IsStatic = false
    for _, sdkinfo := range m.Tasklab.Sdkinfos { // yolo算法
        if sdkinfo.Sdktype == "Yolo" {
            if len(sdkinfo.Sdkdata) > 1 {
                // 大于1才有数据
                fmt.Println("----------------------------------------------------",m.Caddr)
                yoloParam := protomsg.ParamYoloObj{}
                err = proto.Unmarshal(sdkinfo.Sdkdata, &yoloParam)
                if err != nil {
                    fmt.Println("解析YOLO sdk数据时出现错误", err)
                    //continue
                }
                for _, info := range yoloParam.Infos {
                    if info.Typ == 0 {
                        photoMap := ruleserver.PhotoMap{Rects: rectFormat(info.RcObj), Score: float64(info.Prob)*100}
                        arg.Photo = append(arg.Photo, photoMap)
                    }
                }
            } else {
                continue
            }
        }
        //if sdkinfo.Sdktype == "FaceDetect" { // 人脸检测
        //    fmt.Println("数据长度为:", len(sdkinfo.Sdkdata))
        //if sdkinfo.Sdktype == "Yolo" {
        //    if len(sdkinfo.Sdkdata) > 1 {
        //        // 大于1才有数据
        //        fmt.Println("----------------------------------------------------",m.Caddr)
        //        faceParam := protomsg.ParamFacePos{}
        //        err = proto.Unmarshal(sdkinfo.Sdkdata, &faceParam)
        //        yoloParam := protomsg.ParamYoloObj{}
        //        err = proto.Unmarshal(sdkinfo.Sdkdata, &yoloParam)
        //        if err != nil {
        //            fmt.Println("解析FACE sdk数据时出现错误", err)
        //            fmt.Println("解析YOLO sdk数据时出现错误", err)
        //            continue
        //        }
        //        for _, info := range faceParam.Faces {
        //            photoMap := ruleserver.PhotoMap{Rects: ruleserver.Rect{-1, -1, -1, -1}, Score: float64(info.Pos.Quality)}
        //            arg.Photo = append(arg.Photo, photoMap)
        //        for _, info := range yoloParam.Infos {
        //            if info.Typ == 0 {
        //                photoMap := ruleserver.PhotoMap{Rects: rectFormat(info.RcObj), Score: float64(info.Prob)*100,IsYolo:true}
        //                arg.Photo = append(arg.Photo, photoMap)
        //            }
        //        }
        //    } else {
        //        continue
        //    }
        //
        //}
        if sdkinfo.Sdktype == "FaceDetect" { // 人脸检测
            if len(sdkinfo.Sdkdata) > 1 {
                fmt.Println("----------------------------------------------------",m.Caddr)
                faceParam := protomsg.ParamFacePos{}
                err = proto.Unmarshal(sdkinfo.Sdkdata, &faceParam)
                if err != nil {
                    fmt.Println("解析FACE sdk数据时出现错误", err)
                    continue
                }
                for _, info := range faceParam.Faces {
                    photoMap := ruleserver.PhotoMap{Rects: rectFormat(info.Pos.RcFace), Score: float64(info.Pos.Quality)*100, IsYolo:false}
                    arg.Photo = append(arg.Photo, photoMap)
                }
            } else {
                continue
            }
        }
        //if sdkinfo.Sdktype == "FaceExtract" { // 人脸提取
        //}
@@ -132,7 +131,6 @@
// 将外部传进来的rect(top,bottom,left,right)转化为自己内部的rect(left top width height)
func rectFormat(rcobj *protomsg.Rect) ruleserver.Rect {
    rect := ruleserver.Rect{}
    fmt.Println("看一下传入的矩形数据:", rcobj.Left, rcobj.Top, rcobj.Right, rcobj.Bottom)
    rect.X = float64(rcobj.Left)
    rect.Y = float64(rcobj.Top)
    rect.Width = float64(rcobj.Right - rcobj.Left)
ruleserver/ruleToformula.go
@@ -57,10 +57,11 @@
// 每个目标的参数:相似度,占比,尺寸
type Arg struct {
    id         string
    score      float64 // 区域内的目标的相似度
    proportion float64 // 区域内的目标的占比
    size       float64 // 区域内的目标的尺寸
    isYolo       bool    // 是否是yolo数据
    location   Rect    // 记下每个目标的位置参数,最后给结果装配人脸数据的时候用的到
    liker      []LikePerson
}
@@ -80,7 +81,7 @@
    triggerLine   string
    directionLine string
    targetNum     int    // 区域内目标数量
    args          []Arg  // 区域内目标的参数集合
    args          []Arg  // 区域内目标集合
    filterData    []Arg  // 过滤后区域内目标集合
    time          string // 当前时间(用以匹配时间规则)
    keepRight     bool   // 是否靠右行
@@ -89,8 +90,9 @@
// sdk输出的图片上单个目标的数据
type PhotoMap struct {
    Rects Rect    // 矩形区域参数
    Score float64 // 相似度得分(有多大程度像一个目标。人脸,人体或车等等)
    Rects Rect         // 矩形区域参数
    Score float64      // 相似度得分(有多大程度像一个目标。人脸,人体或车等等)
    IsYolo bool           // 是否是yolo数据
    Liker []LikePerson // 如果是人脸的话尤其是比对,应存下他跟底库的人员的相似情况 yolo的话给nil就行
}
@@ -117,9 +119,17 @@
// 过规则库打上的标签
type Result struct {
    TaskId      string // 任务id
    IsYolo        bool   // 是否是yolo触发的规则
    RuleGroupId string // 规则组id
    AlarmLevel  int32  // 报警等级
    RuleText    string // 文字版规则组
    Faces       []Face // 触发此规则组的人脸(人脸系列规则专用,yolo系列不需要)
}
//
type Face struct {
    Location Rect         // 人脸坐标框
    liker    []LikePerson // 相似人员(如果是单纯的人脸检测可无此项)
}
// 包含N条规则元素的一整条规则
@@ -155,6 +165,7 @@
        areaMap.CountAreaObjs(arg)
        list.areaMapList = append(list.areaMapList, areaMap)
    }
    //fmt.Println("为每个摄像机区域填充数据后的内容", list.areaMapList)
    // 将此帧数据按摄像机区域分类打包后判断是否报警
    judge(&list, arg)
@@ -178,17 +189,16 @@
    // }
    for _, obj := range arg.Photo {
        if threshold <= obj.Score &&
            size <= float64(obj.Rects.Width*obj.Rects.Height) &&
            intersectionper <= 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{score: obj.Score, proportion: PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale), size: float64(obj.Rects.Width * obj.Rects.Height)}
            log.Println("放进去的arg:-------",arg1)
            arg1 := Arg{ obj.Score,PgsInterPercent(areaPoints, obj.Rects, widthScale, heigthScale), float64(obj.Rects.Width * obj.Rects.Height),  obj.IsYolo,obj.Rects, obj.Liker}
            log.Println("放进去的arg:-------", arg1)
            a.args = append(a.args, arg1)
            a.filterData = append(a.filterData, arg1)
        }
    }
    a.time = time.Unix(time.Now().Unix(), 0).String()[11:16]
    a.keepRight = arg.KeepRight
    a.isStatic = arg.IsStatic
@@ -309,6 +319,7 @@
// 独立任务
func singleTask(aml *AreaMapList, arg *ArgsFromSdk, groupRule *protomsg.GroupRule, taskId string) bool {
    var completeFormula string = ""
    var okSdks string = "" // 记录下触发规则的是yolo还是face
    for _, areaMap := range aml.areaMapList {
        //fmt.Println("当前规则组为---------:",groupRule)
        for j := 0; j < len(groupRule.Rules); j++ {
@@ -317,7 +328,10 @@
        }
        for j := 0; j < len(groupRule.Rules); j++ {
            // 再过其他数据 这步直接得到结果(真或假) 过目标数量
            flag := transferParameters(groupRule.Rules[j], &areaMap)
            flag,sdk := transferParameters(groupRule.Rules[j], &areaMap)
            if flag == "true" {
                okSdks = okSdks + "," + sdk
            }
            if flag != "" {
                fmt.Println("得出的结果", flag)
                completeFormula = completeFormula + groupRule.Rules[j].RuleWithPre + "" + flag
@@ -329,6 +343,7 @@
                fmt.Println("强行拼凑一个人数是否大于0的结果", flag)
                completeFormula = flag
            }
            okSdks = okSdks + "," + "yolo"
        }
        for j := 0; j < len(groupRule.Rules); j++ {
            // 这步过的是时间规则(时间段等)
@@ -359,7 +374,7 @@
            }
            return false
        } else {
            // 去看定时器此时是否走到0,走到0的话返回成功报警
            // 去看池子里是否有与本帧数据有关的定时器,如果有,看此时是否走到0,没有此定时器或有定时器走到0的话返回成功报警
            var flag bool = true
            for k, timeEle := range TimeEleList {
                if strings.Index(k, taskId) != -1 {
@@ -369,9 +384,24 @@
                }
            }
            if flag {
                fmt.Println("定时器报警了")
                // 过完规则后打个标签,告诉调用者本帧数据针对哪个任务哪组规则报警了
                arg.RuleResult = append(arg.RuleResult, Result{taskId, groupRule.GroupId, groupRule.AlarmLevel, groupRule.GroupText})
                fmt.Println("本帧数据符合规则")
                // 取出所有区域内经过过滤后还剩下的人脸,放进结果中
                faces := []Face{}
                for _, areaMap := range aml.areaMapList {
                    if len(areaMap.filterData) > 0 {
                        for _, arg := range areaMap.filterData {
                            faces = append(faces,Face{arg.location,arg.liker})
                        }
                    }
                }
                // 如果对yolo和人脸都报警,那就打两遍标签
                if strings.Contains(okSdks,"yolo") {
                    // 过完规则后打个标签,告诉调用者本帧数据针对哪个任务哪组规则报警了     后加:可能还不够,还需要区分触发报警的对象,后面往es数据库插数据时要用
                    arg.RuleResult = append(arg.RuleResult, Result{taskId, true,groupRule.GroupId, groupRule.AlarmLevel, groupRule.GroupText, faces})
                }
                if strings.Contains(okSdks,"face") {
                    arg.RuleResult = append(arg.RuleResult, Result{taskId, false,groupRule.GroupId, groupRule.AlarmLevel, groupRule.GroupText, faces})
                }
                return true
            } else {
                return false
@@ -419,14 +449,12 @@
            }
            // 先清空过滤后的数据,再往里塞本次过滤后的数据
            am.filterData = am.filterData[0:0]
            log.Println("看一下当前小规则:",*rule)
            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 // 得到字符串公式
                    fmt.Println("占比的字符串公式:--------",formula)
                } else {
                    formula = strconv.FormatFloat(arg.size, 'f', -1, 64) + " " + rule.Operator + " " + rule.SdkArgValue // 得到字符串公式
                }
@@ -465,57 +493,92 @@
    }
}
// 冗余拼接
// 冗余拼接一个目标数量大于0
func splice1(am *AreaMap) string {
    args := am.targetNum
    log.Println("看看区域内目标数量:----------",args)
    formula := strconv.Itoa(args) + " " + ">" + "0"
    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)                      // 得到数学公式的结果
    result, _ := expression.Evaluate(nil)          // 得到数学公式的结果
    return strconv.FormatBool(result.(bool))
}
// 给数据库的规则表达式代参 args: 一条子规则,区域数据
func transferParameters(rule *protomsg.Rule, am *AreaMap) string {
func transferParameters(rule *protomsg.Rule, am *AreaMap) (string,string) {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上
        if rule.SdkArgAlias == "targetNum" { // 如果参数是要区域内目标数量 即yolo
        if rule.SdkArgAlias == "targetNum" { // 如果参数是要区域内目标数量 即yolo 人脸不会有数量
            //fmt.Println("得出结果阶段", "比较的规则是:", rule)
            if rule.Operator == "" {
                return strconv.Itoa(am.targetNum) // 如果后面不跟操作符就直接返回数量  比如要跟下一个区域比较数量的就直接返回本区域的数量
                return strconv.Itoa(am.targetNum),"" // 如果后面不跟操作符就直接返回数量  比如要跟下一个区域比较数量的就直接返回本区域的数量
            }
            args := am.targetNum
            formula := strconv.Itoa(args) + " " + rule.Operator + " " + rule.SdkArgValue
            //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 strconv.FormatBool(result.(bool))
            return strconv.FormatBool(result.(bool)),""
            // 加上关于算法的判断条件,不能只有关于规则的,有的算法本身就是一个规则,如个体静止,靠右行,所以,拿到当前子规则的sdkid来判断是否是那些特殊的规则
        } else if rule.SdkId == "IsStatic" { // 静止算法
            if am.isStatic {
                return "true"
                return "true","static"
            } else {
                return "false"
                return "false","static"
            }
        } else if rule.SdkId == "KeepRight" { // 靠右行算法
            if am.keepRight {
                return "true"
                return "true","keepRight"
            } else {
                return "false"
                return "false","keepRight"
            }
        } else if rule.SdkId == "FaceDetect" { // 人脸检测
            if rule.Operator == "==" || rule.Operator == ">=" || rule.Operator == "<=" || rule.Operator == "<" || rule.Operator == ">" || rule.Operator == "!=" {
                // 如果是不规矩的连接符统统返回false 规则也只能判断人脸的相似度,所以不存在别的连接符
                return "false"
                return "false","face"
            } else {
                return "false"
                return "false","face"
            }
        } else if rule.SdkId == "FaceCompare"{
            // 只需要过滤阈值,过滤完后数组长度大于0即为报警,但如何对每一张都报警呢
        } else if rule.SdkId == "FaceCompare" {
            // 只需要过滤阈值,过滤完后数组长度大于0即为报警,但要考虑如何对每一张都报警呢
            argValue, err := strconv.ParseFloat(rule.SdkArgValue, 64)
            if err != nil {
                log.Println("规则配置的阈值非法")
                return "false","face"
            }
            flag := "false"
            for _, obj := range am.filterData {
                if !obj.isYolo {  // 人脸数据才过滤相似者
                    for index := 0; index < len(obj.liker); {
                        // 将达不到阈值的相似者从相似者数组中删除
                        if obj.liker[index].Score < argValue {
                            // Go 语言中切片删除元素的本质是:以被删除元素为分界点,将前后两个部分的内存重新连接起来。不用怀疑,数组删除元素就这么坑爹
                            obj.liker = append(obj.liker[:index], obj.liker[index+1:]...)
                        } else {
                            index++
                        }
                    }
                }
            }
            for _,obj2 := range am.filterData {
                if !obj2.isYolo && len(obj2.liker) > 0 {
                    flag = "true"
                }
            }
            return flag,"face"
        }
    }
    return ""
    return "",""
}
func timeRuleResult(rule *protomsg.Rule, am *AreaMap) string {
    if rule.PolygonId == am.areaId { // 首先规则所对应的区域id要跟区域数据的id对的上