LingxinMeng 5 hónapja
szülő
commit
ff29acc090

+ 231 - 0
aarch64/geely/common/config/c_cloud.go

@@ -0,0 +1,231 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"gopkg.in/yaml.v3"
+	"os"
+	"sync"
+	"time"
+)
+
+type MonitorStruct struct {
+	Url string `yaml:"url"`
+}
+
+type platform struct {
+	UrlDeviceAuth string `yaml:"url-device-auth"`
+	UrlTaskPoll   string `yaml:"url-task-poll"`
+	UrlTask       string `yaml:"url-task"`
+}
+
+type rosbagStruct struct {
+	Path string   `yaml:"path"`
+	Envs []string `yaml:"envs"`
+}
+
+type hostStruct struct {
+	Name   string       `yaml:"name"`
+	Ip     string       `yaml:"ip"`
+	Topics []string     `yaml:"topics"`
+	Rosbag rosbagStruct `yaml:"rosbag"`
+}
+type ros struct {
+	MasterAddress string   `yaml:"master-address"`
+	Nodes         []string `yaml:"nodes"`
+}
+type disk struct {
+	Name string   `yaml:"name"`
+	Used uint64   `yaml:"used"`
+	Path []string `yaml:"path"`
+}
+
+type trigger struct {
+	Label  string   `yaml:"label"`
+	Topics []string `yaml:"topics"`
+}
+
+type CloudConfigStruct struct {
+	RefreshCloudConfig    bool          `yaml:"refresh-cloud-config"`
+	CompressBag           bool          `yaml:"compress-bag"`
+	CleanBeforeStart      bool          `yaml:"clean-before-start"`
+	FullCollect           bool          `yaml:"full-collect"`
+	ConfigRefreshInterval int           `yaml:"config-refresh-interval"` // 配置刷新时间间隔
+	BagNumber             int           `yaml:"bag-number"`
+	TimeWindowSendGap     int           `yaml:"time-window-send-gap"` // 主节点向从节点发送窗口的最小时间间隔
+	TimeToLabelJsonPath   string        `yaml:"time-to-label-json-path"`
+	BagDataDir            string        `yaml:"bag-data-dir"`
+	BagCopyDir            string        `yaml:"bag-copy-dir"`
+	TriggersDir           string        `yaml:"triggers-dir"`
+	TcpPort               string        `yaml:"tcp-port"`
+	RpcPort               string        `yaml:"rpc-port"`
+	Triggers              []trigger     `yaml:"triggers"`
+	Hosts                 []hostStruct  `yaml:"hosts"`
+	Ros                   ros           `yaml:"ros"`
+	Platform              platform      `yaml:"platform"`
+	Disk                  disk          `yaml:"disk"`
+	Monitor               MonitorStruct `yaml:"monitor"`
+}
+
+var (
+	CloudConfig      CloudConfigStruct
+	CloudConfigMutex sync.RWMutex
+)
+
+// 初始化业务配置
+func InitCloudConfig() {
+	c_log.GlobalLogger.Info("初始化OSS配置文件 - 开始。")
+	var content []byte // cloud.yaml 内容
+	var err error
+	if LocalConfig.Internet {
+		c_log.GlobalLogger.Info("车辆可以访问互联网。")
+		// 获取文件的目录
+		_ = util.CreateParentDir(LocalConfig.CloudConfigLocalPath)
+		// 3 ------- 获取 yaml 字符串 -------
+		cloudConfigObjectKey := LocalConfig.OssBasePrefix + LocalConfig.EquipmentNo + "/" + LocalConfig.CloudConfigFilename
+		// todo 等待时间同步
+		// 判断文件是否存在。如果不存在则使用默认的
+		isExist, err := OssBucket.IsObjectExist(cloudConfigObjectKey)
+		if err != nil {
+			c_log.GlobalLogger.Errorf("判断配置文件是否存在失败,错误信息为:%v", err)
+		}
+		if isExist {
+			c_log.GlobalLogger.Info("使用机器人自定义配置文件:", cloudConfigObjectKey)
+		} else {
+			cloudConfigObjectKey = LocalConfig.OssBasePrefix + LocalConfig.CloudConfigFilename // 默认配置文件路径
+			c_log.GlobalLogger.Info("使用默认配置文件:", cloudConfigObjectKey)
+		}
+		for {
+			OssMutex.Lock()
+			err := OssBucket.GetObjectToFile(cloudConfigObjectKey, LocalConfig.CloudConfigLocalPath)
+			OssMutex.Unlock()
+			if err != nil {
+				c_log.GlobalLogger.Error("下载oss上的配置文件 "+cloudConfigObjectKey+" 失败。网络授时未完成或者未配置阿里云网络映射到/etc/hosts:", err)
+				time.Sleep(time.Duration(2) * time.Second)
+				continue
+			}
+			break
+		}
+		if CloudConfig.RefreshCloudConfig {
+			go RefreshCloudConfig()
+		}
+	} else {
+		c_log.GlobalLogger.Infof("车辆不可以访问互联网,请提前将配置文件放到【%v】。", LocalConfig.CloudConfigLocalPath)
+		for {
+			time.Sleep(time.Duration(1) * time.Second)
+			if util.FileExists(LocalConfig.CloudConfigLocalPath) {
+				c_log.GlobalLogger.Infof("配置文件【%v】已准备好。", LocalConfig.CloudConfigLocalPath)
+				break
+			} else {
+				c_log.GlobalLogger.Infof("配置文件【%v】没有准备好。", LocalConfig.CloudConfigLocalPath)
+				continue
+			}
+		}
+	}
+
+	content, err = os.ReadFile(LocalConfig.CloudConfigLocalPath)
+	if err != nil {
+		c_log.GlobalLogger.Error("程序退出。配置文件 ", LocalConfig.CloudConfigLocalPath, " 读取失败:", err)
+		os.Exit(-1)
+	}
+
+	// 4 ------- 解析YAML内容 -------
+	var newCloudConfig CloudConfigStruct
+	err = yaml.Unmarshal(content, &newCloudConfig)
+	if err != nil {
+		c_log.GlobalLogger.Error("程序退出。配置文件 ", LocalConfig.CloudConfigLocalPath, " 解析失败:", err)
+		os.Exit(-1)
+	}
+
+	// 5 ------- 校验 yaml -------
+	if checkConfig(newCloudConfig) {
+		CloudConfigMutex.RLock()
+		CloudConfig = newCloudConfig
+		CloudConfigMutex.RUnlock()
+	} else {
+		c_log.GlobalLogger.Error("配置文件格式错误:", newCloudConfig)
+		os.Exit(-1)
+	}
+	c_log.GlobalLogger.Info("初始化OSS配置文件 - 成功。")
+	util.CreateDir(CloudConfig.BagDataDir)
+	util.CreateDir(CloudConfig.BagCopyDir)
+
+	_ = util.RemoveSubFiles(CloudConfig.BagDataDir)
+	if CloudConfig.CleanBeforeStart { // 判断是否需要清空原数据缓存
+		if LocalConfig.Node.Name == "node1" { // 只有cloud配置了同时节点为node1才清除
+			// 清空 timeToLabel.json
+			timeToLabelJson, _ := util.MapToJsonString(map[string]interface{}{"time": "label"})
+			_ = util.WriteFile(timeToLabelJson, CloudConfig.TimeToLabelJsonPath)
+			c_log.GlobalLogger.Info("清空timeToLabel.json - 成功。")
+		}
+		_ = util.RemoveSubFiles(CloudConfig.BagCopyDir)
+	}
+}
+
+// 刷新配置文件
+func refreshCloudConfig() {
+	// 获取文件的目录
+	_ = util.CreateParentDir(LocalConfig.CloudConfigLocalPath)
+	// 3 ------- 获取 yaml 字符串 -------
+	var content []byte
+	cloudConfigObjectKey := LocalConfig.OssBasePrefix + LocalConfig.EquipmentNo + "/" + LocalConfig.CloudConfigFilename
+	// 判断文件是否存在。如果不存在则使用默认的
+	isExist, err := OssBucket.IsObjectExist(cloudConfigObjectKey)
+	if err != nil {
+		c_log.GlobalLogger.Errorf("判断配置文件是否存在失败,错误信息为:%v", err)
+		return
+	}
+	if !isExist {
+		cloudConfigObjectKey = LocalConfig.OssBasePrefix + LocalConfig.CloudConfigFilename // 默认配置文件路径
+	}
+	OssMutex.Lock()
+	err = OssBucket.GetObjectToFile(cloudConfigObjectKey, LocalConfig.CloudConfigLocalPath)
+	OssMutex.Unlock()
+	if err != nil {
+		c_log.GlobalLogger.Error("下载oss上的配置文件"+cloudConfigObjectKey+"失败。", err)
+		return
+	}
+
+	content, err = os.ReadFile(LocalConfig.CloudConfigLocalPath)
+	if err != nil {
+		c_log.GlobalLogger.Error("配置文件 ", LocalConfig.CloudConfigLocalPath, " 读取失败:", err)
+		return
+	}
+
+	// 4 ------- 解析YAML内容 -------
+	var newCloudConfig CloudConfigStruct
+	err = yaml.Unmarshal(content, &newCloudConfig)
+	if err != nil {
+		c_log.GlobalLogger.Error("配置文件 ", LocalConfig.CloudConfigLocalPath, " 解析失败:", err)
+		return
+	}
+
+	// 5 ------- 校验 yaml -------
+	if checkConfig(newCloudConfig) {
+		CloudConfigMutex.RLock()
+		CloudConfig = newCloudConfig
+		CloudConfigMutex.RUnlock()
+	} else {
+		c_log.GlobalLogger.Error("配置文件格式错误:", newCloudConfig)
+		return
+	}
+	util.CreateDir(CloudConfig.BagDataDir)
+	util.CreateDir(CloudConfig.BagCopyDir)
+}
+
+// RefreshCloudConfig 轮询oss上的配置文件更新到本地
+func RefreshCloudConfig() {
+	for {
+		time.Sleep(time.Duration(CloudConfig.ConfigRefreshInterval) * time.Second)
+		refreshCloudConfig()
+	}
+}
+
+// CheckConfig 校验 cfg.yaml 文件
+func checkConfig(check CloudConfigStruct) bool {
+	if len(check.Hosts) != 2 {
+		c_log.GlobalLogger.Error("cloud-config.yaml中配置的hosts必须为2。")
+		os.Exit(-1)
+	}
+	return true
+}

+ 21 - 0
aarch64/geely/common/config/c_killrpcserver.go

@@ -0,0 +1,21 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/config/c_log"
+	"net"
+	"os"
+)
+
+var KillSignalListener net.Listener
+
+func InitKillSignalListener(serverIp string) {
+	var err error
+	c_log.GlobalLogger.Info("初始化RPC端口监听Kill信号 - 开始。")
+	socket := serverIp + ":" + CloudConfig.RpcPort
+	KillSignalListener, err = net.Listen("tcp", socket)
+	if err != nil {
+		c_log.GlobalLogger.Error("监听rpc端口失败:", err)
+		os.Exit(-1)
+	}
+	c_log.GlobalLogger.Info("初始化RPC端口监听Kill信号 - 成功:", socket)
+}

+ 64 - 0
aarch64/geely/common/config/c_oss.go

@@ -0,0 +1,64 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"encoding/json"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"os"
+	"sync"
+	"time"
+)
+
+type OssConnectInfoStruct struct {
+	Endpoint        string `json:"endpoint"`
+	AccessKeyId     string `json:"accessKeyId"`
+	AccessKeySecret string `json:"accessKeySecret"`
+	BucketName      string `json:"bucketName"`
+}
+
+var (
+	OssClient *oss.Client
+	OssBucket *oss.Bucket
+	OssMutex  sync.Mutex
+)
+
+func InitOssConfig() {
+	if !LocalConfig.Internet { // 如果没有网,就是完全用不了oss,直接不初始化
+		return
+	}
+	var err error
+	c_log.GlobalLogger.Info("初始化OSS客户端对象 - 开始。")
+	// 1 访问 HTTP 服务获取 OSS 配置
+	var get string
+	// 等待网络可达
+	for {
+		get, err = util.HttpGet(LocalConfig.UrlGetOssConfig)
+		if err != nil {
+			c_log.GlobalLogger.Error("http 获取 oss 配置时出错。可能需要等待网络初始化完成:", err)
+			time.Sleep(time.Duration(2) * time.Second)
+			continue
+		}
+		break
+	}
+	var ossConnectInfo OssConnectInfoStruct
+	err = json.Unmarshal([]byte(get), &ossConnectInfo)
+	if err != nil {
+		c_log.GlobalLogger.Error("解析json时出错:", err)
+		os.Exit(-1)
+	}
+
+	OssClient, err = oss.New(ossConnectInfo.Endpoint, ossConnectInfo.AccessKeyId, ossConnectInfo.AccessKeySecret, oss.UseCname(true))
+	if err != nil {
+		c_log.GlobalLogger.Error("无法创建阿里云client:", err)
+		os.Exit(-1)
+	}
+	// 超时时间设置
+	//OssClient.Config.Timeout = 3600
+	OssBucket, err = OssClient.Bucket(ossConnectInfo.BucketName)
+	if err != nil {
+		c_log.GlobalLogger.Error("无法创建阿里云bucket:", err)
+		os.Exit(-1)
+	}
+	c_log.GlobalLogger.Info("初始化OSS客户端对象 - 成功。")
+}

+ 197 - 0
aarch64/geely/common/config/c_platform.go

@@ -0,0 +1,197 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"encoding/json"
+	"strings"
+	"time"
+)
+
+type taskTrigger struct {
+	TriggerId         int    `json:"triggerId"`
+	TriggerName       string `json:"triggerName"`
+	TriggerScriptPath string `json:"triggerScriptPath"`
+	TriggerType       string `json:"triggerType"`
+}
+
+type PlatformConfigStruct struct {
+	TaskConfigId    string        `json:"taskConfigId"`   // 配置ID
+	TaskConfigName  string        `json:"taskConfigName"` // 配置名称
+	DropUploadData  bool          `json:"dropUploadData"` // 更新任务时 true 先上传旧任务 false 删除旧任务
+	TaskMaxTime     int           `json:"taskMaxTime"`
+	TaskBeforeTime  int           `json:"taskBeforeTime"`
+	TaskAfterTime   int           `json:"taskAfterTime"`
+	TaskCachePolicy string        `json:"taskCachePolicy"`
+	EquipmentTopic  string        `json:"equipmentTopic"` // topic序列
+	Lru             []string      `json:"LRU"`
+	TaskTriggers    []taskTrigger `json:"taskTriggers"`
+}
+
+type response struct {
+	Data    PlatformConfigStruct `json:"data"`
+	Success bool                 `json:"success"`
+	Message string               `json:"message"`
+	Code    int                  `json:"code"`
+	NowTime string               `json:"nowTime"`
+}
+
+var (
+	PlatformConfig PlatformConfigStruct
+	RecordTopics   []string
+)
+
+// 初始化数据闭环平台的配置
+func InitPlatformConfig() {
+	if !LocalConfig.Internet {
+		return
+	}
+	var err error
+	c_log.GlobalLogger.Info("获取数据闭环平台配置 - 开始")
+	// 1 如果车辆没有配置任务,则阻塞在这里,不启动任务
+	for {
+		time.Sleep(time.Duration(2) * time.Second)
+		PlatformConfig, err = getConfig()
+		if err != nil {
+			c_log.GlobalLogger.Error("获取配置status失败:", err)
+			continue
+		}
+		if checkPlatformConfig() {
+			RecordTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
+			// 去掉首尾空格
+			for i, topic := range RecordTopics {
+				RecordTopics[i] = strings.TrimSpace(topic)
+			}
+			break
+		}
+	}
+	c_log.GlobalLogger.Infof("获取数据闭环平台配置 - 成功。【触发前】=【%v 秒】,触发后=【%v 秒】,【最大窗口】=【%v 秒】。", PlatformConfig.TaskBeforeTime, PlatformConfig.TaskAfterTime, PlatformConfig.TaskMaxTime)
+}
+
+/*
+	{
+	  "data": {
+	    "accessToken": "YWRmYWRzZmFzZGZhZHNmYWRmYWRm=",
+	    "expireTime": "28800",
+	    "equipmentNo": "robot-001"
+	  },
+	  "success": true,
+	  "message": "ok",
+	  "code": 1,
+	  "nowTime": "2023-12-09 22:41:00"
+	}
+*/
+// 认证接口,获取access_token
+func GetAccessToken() (string, error) {
+	url := &CloudConfig.Platform.UrlDeviceAuth
+	param := &map[string]string{
+		"equipmentNo": LocalConfig.EquipmentNo,
+		"secretKey":   LocalConfig.SecretKey,
+	}
+	respJson, err := util.HttpPostJsonResponseString(
+		*url,
+		*param,
+	)
+	if err != nil {
+		return "", nil
+	}
+	respMap, err := util.JsonStringToMap(respJson)
+	if err != nil {
+		c_log.GlobalLogger.Errorf("解析返回结果【%v】失败,请求地址为【%v】,请求参数为【%v】:%v", respJson, *url, *param, err)
+		return "", nil
+	}
+
+	dataMap, ok := respMap["data"].(map[string]interface{})
+	if !ok {
+		c_log.GlobalLogger.Error("解析返回结果.data", dataMap, "失败:", err)
+		return "", nil
+	}
+	return dataMap["accessToken"].(string), nil
+}
+
+/*
+	{
+	  "data": {
+	    "status": "UNCHANGE"
+	    "taskConfigld": "xxx"
+	  },
+	  "success": true,
+	  "message": "ok",
+	  "code": 1,
+	  "nowTime": "2023-12-09 21:08:49"
+	}
+*/
+//GetStatus 根据taskConfigId获取任务status,如果传入空代表车端没有配置,直接获取新的配置
+func GetStatus(taskConfigId string) (string, error) {
+	token, err := GetAccessToken()
+	if err != nil {
+		return "", err
+	}
+	resp, err := util.HttpGetStringAddHeadersResponseString(
+		CloudConfig.Platform.UrlTaskPoll,
+		map[string]string{
+			"authorization": token,
+		},
+		map[string]string{
+			"equipmentNo":  LocalConfig.EquipmentNo,
+			"taskConfigId": taskConfigId,
+		},
+	)
+
+	if err != nil {
+		c_log.GlobalLogger.Error("访问接口", CloudConfig.Platform.UrlTask, "失败:", err)
+		return "", err
+	}
+	respMap, err := util.JsonStringToMap(resp)
+	if err != nil {
+		c_log.GlobalLogger.Error("解析【返回结果1】", resp, "失败:", err)
+		return "", err
+	}
+	dataMap, ok := respMap["data"].(map[string]interface{})
+	if !ok {
+		c_log.GlobalLogger.Errorf("解析【返回结果.data】的类型不是(map[string]interface{}),【dataMap】=%v", dataMap)
+		return "", err
+	}
+	return dataMap["status"].(string), nil
+}
+
+func getConfig() (PlatformConfigStruct, error) {
+	token, err := GetAccessToken()
+	if err != nil {
+		return PlatformConfigStruct{}, err
+	}
+	// 下载插件和获取配置
+	// 2 访问配置获取接口
+	resp, err := util.HttpGetStringAddHeadersResponseString(
+		CloudConfig.Platform.UrlTask,
+		map[string]string{
+			"authorization": token,
+		},
+		map[string]string{
+			"equipmentNo": LocalConfig.EquipmentNo,
+		},
+	)
+	if err != nil {
+		c_log.GlobalLogger.Error("访问接口", CloudConfig.Platform.UrlTask, "失败:", err)
+		return PlatformConfigStruct{}, err
+	}
+	var result response
+	err = json.Unmarshal([]byte(resp), &result)
+	if err != nil {
+		c_log.GlobalLogger.Error("解析【返回结果】", resp, "失败:", err)
+		return PlatformConfigStruct{}, err
+	}
+	return result.Data, nil
+}
+
+func checkPlatformConfig() bool {
+	if PlatformConfig.TaskConfigId == "" {
+		c_log.GlobalLogger.Error("数据闭环平台没有配置任务。")
+		return false
+	}
+	if PlatformConfig.EquipmentTopic == "" {
+		c_log.GlobalLogger.Error("数据闭环平台没有配置topic序列。")
+		return false
+	}
+	return true
+}

+ 71 - 0
aarch64/geely/common/config/c_resource.go

@@ -0,0 +1,71 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/util"
+	"encoding/json"
+	"fmt"
+	"github.com/sirupsen/logrus"
+	"os"
+	"path/filepath"
+	"runtime"
+	"time"
+)
+
+var resourceLogger *logrus.Logger
+
+// 保存资源占用情况
+func SendResourceUsage() {
+	initLog(LogDir, ResourceLogPrefix)
+	for {
+		time.Sleep(time.Duration(10) * time.Second)
+		top10Cpu, top10Mem := util.GetTop10CpuAndMem()
+		top10CpuJson, _ := json.MarshalIndent(top10Cpu, "", "    ")
+		top10MemJson, _ := json.MarshalIndent(top10Mem, "", "    ")
+
+		requestMap := map[string]string{
+			"totalCpuUsage":    util.ToString(util.GetCpuPercent()),
+			"totalMemoryUsage": util.ToString(util.GetMemoryPercent()),
+			"top10Process":     string(top10CpuJson),
+			"top10Cpu":         string(top10CpuJson),
+			"top10Mem":         string(top10MemJson),
+			"deviceNumber":     LocalConfig.EquipmentNo,
+			"socIp":            LocalConfig.Node.Ip,
+		}
+		resourceLogger.Infof("监控信息为:%v", util.MapToReadableJsonString(requestMap))
+		//responseString, err := util.HttpPostJsonWithHeaders(
+		//	CloudConfig.Monitor.Url,
+		//	map[string]string{"Authorization": "U9yKpD6kZZDDe4LFKK6myAxBUT1XRrDM"},
+		//	requestMap,
+		//)
+		//if err != nil {
+		//	c_log.GlobalLogger.Errorf("发送数据监控信息报错%v,响应信息为:%v", err, responseString)
+		//}
+		//c_log.GlobalLogger.Infof("发送数据监控信息成功,响应信息为:%v", responseString)
+	}
+}
+
+// 初始化日志配置
+func initLog(logDir string, prefix string) {
+	time.Sleep(time.Duration(1) * time.Second)
+	// 创建、追加、读写,777,所有权限
+	logPath := logDir + prefix + "-" + time.Now().Format("2006-01-02-15-04-05") + ".log"
+	err := util.CreateParentDir(logPath)
+	if err != nil {
+		os.Exit(-1)
+	}
+	f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
+	if err != nil {
+		os.Exit(-1)
+	}
+	resourceLogger = logrus.New()
+	resourceLogger.SetOutput(f)
+	resourceLogger.SetReportCaller(true) // 开启行号显示
+	resourceLogger.SetFormatter(&logrus.JSONFormatter{
+		CallerPrettyfier: func(frame *runtime.Frame) (string, string) {
+			fileName := filepath.Base(frame.File)
+			return "", fmt.Sprintf("%s:%d", fileName, frame.Line)
+		},
+	})
+	resourceLogger.Info("初始化resourceLogger - 成功")
+
+}

+ 38 - 0
aarch64/geely/common/config/c_ros.go

@@ -0,0 +1,38 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"github.com/bluenviron/goroslib/v2"
+	"time"
+)
+
+var (
+	RosNode    *goroslib.Node
+	RosbagPath string
+	RosbagEnvs []string
+)
+
+func InitRosConfig() {
+	var err error
+	// 1
+	c_log.GlobalLogger.Info("初始化RosNode - 开始")
+	for {
+		time.Sleep(time.Duration(2) * time.Second)
+		if RosNode, err = goroslib.NewNode(goroslib.NodeConf{Name: "node" + util.GetNowTimeCustom(), MasterAddress: CloudConfig.Ros.MasterAddress}); err != nil {
+			c_log.GlobalLogger.Info("初始化RosNode - 进行中:", err)
+			continue
+		}
+		break
+	}
+	c_log.GlobalLogger.Info("初始化RosNode - 成功:", CloudConfig.Ros.MasterAddress)
+	// 2 获取 rosbag 命令路径和环境变量
+	for _, host := range CloudConfig.Hosts {
+		if host.Name == LocalConfig.Node.Name {
+			RosbagPath = host.Rosbag.Path
+			RosbagEnvs = host.Rosbag.Envs
+			break
+		}
+	}
+	c_log.GlobalLogger.Infof("rosbag 命令路径为:%v,环境变量为:%v", RosbagPath, RosbagEnvs)
+}

+ 92 - 0
aarch64/geely/common/service/disk_clean.go

@@ -0,0 +1,92 @@
+package service
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	masterConfig "cicv-data-closedloop/aarch64/pjisuv/master/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/domain"
+	"cicv-data-closedloop/common/entity"
+	"cicv-data-closedloop/common/util"
+	"time"
+)
+
+// DiskClean 如果磁盘占用过高,则删除timeWindow和对应的文件
+// todo 这里依赖了主节点的触发器映射,需要传给从节点一份
+func DiskClean() {
+	c_log.GlobalLogger.Info("启动timeWindow清理goroutine,根据缓存策略清理copy目录。")
+	for {
+		time.Sleep(1000 * time.Millisecond)
+		bags, _ := util.ListAbsolutePathWithSuffixAndSort(commonConfig.CloudConfig.BagCopyDir, ".bag")
+		if len(bags) == 0 {
+			continue
+		}
+		/*
+			TTL(0, "删除旧数据");
+			STOP(1, "停止缓存");
+			LRU(2, "保留高优先级")
+		*/
+		policyToDescription := map[string]string{
+			"TTL":  "删除旧数据",
+			"STOP": "停止缓存",
+			"LRU":  "保留高优先级",
+		}
+		// 1 获取磁盘占用
+		diskUsed, _ := util.GetDiskUsed(commonConfig.CloudConfig.Disk.Name)
+		if diskUsed > commonConfig.CloudConfig.Disk.Used {
+			policy := commonConfig.PlatformConfig.TaskCachePolicy
+			c_log.GlobalLogger.Errorf("磁盘占用 %v 超过 %v,触发删除规则 %v", diskUsed, commonConfig.CloudConfig.Disk.Used, policyToDescription[policy])
+			if policy == "TTL" {
+				// 1 获取时间窗口队列中的第二个
+				if len(entity.TimeWindowConsumerQueue) > 2 {
+					deleteTimeWindow(1)
+				}
+			} else if policy == "STOP" {
+				// 2 获取时间窗口队列中的倒数第一个
+				if len(entity.TimeWindowConsumerQueue) > 2 {
+					deleteTimeWindow(len(entity.TimeWindowConsumerQueue) - 1)
+				}
+			} else if policy == "LRU" {
+				// 3 获取优先级最低的时间窗口
+				if len(entity.TimeWindowConsumerQueue) > 2 {
+					indexToRemove := getIndexToRemoveForLRU()
+					if indexToRemove != -1 {
+						deleteTimeWindow(indexToRemove)
+					}
+				}
+			} else {
+				c_log.GlobalLogger.Error("未知的缓存策略:", policy)
+			}
+		}
+	}
+}
+
+func deleteTimeWindow(indexToRemove int) {
+	timeWindowToRemove := entity.TimeWindowConsumerQueue[indexToRemove]
+	// 1 删除队列中的窗口。使用切片的特性删除指定位置的元素
+	entity.TimeWindowConsumerQueueMutex.Lock()
+	entity.TimeWindowConsumerQueue = append(entity.TimeWindowConsumerQueue[:indexToRemove], entity.TimeWindowConsumerQueue[indexToRemove+1:]...)
+	entity.TimeWindowConsumerQueueMutex.Unlock()
+	// 2 删除该窗口对应的文件目录。
+	faultTime := timeWindowToRemove.FaultTime
+	dir := domain.GetCopyDir(commonConfig.CloudConfig.BagCopyDir, faultTime)
+	err := util.RemoveDir(dir)
+	if err != nil {
+		c_log.GlobalLogger.Error("删除目录", dir, "失败:", err)
+	}
+}
+
+func getIndexToRemoveForLRU() int {
+	lru := commonConfig.PlatformConfig.Lru
+	i := len(lru) - 1
+	for i >= 0 {
+		for i2, window := range entity.TimeWindowConsumerQueue {
+			for _, label := range window.Labels {
+				value, _ := masterConfig.LabelMapTriggerId.Load(label)
+				if value == lru[i] {
+					return i2
+				}
+			}
+		}
+	}
+	return -1
+}

+ 115 - 0
aarch64/geely/common/service/kill_self.go

@@ -0,0 +1,115 @@
+package service
+
+import (
+	"cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"net/rpc"
+	"os"
+	"sync"
+	"time"
+)
+
+var (
+	ChannelKillRosRecord      = make(chan int)
+	ChannelKillDiskClean      = make(chan int)
+	ChannelKillWindowProducer = make(chan int)
+	ChannelKillMove           = make(chan int)
+	ChannelKillConsume        = make(chan int)
+
+	KillChannel = 5
+	KillTimes   = 0
+	MutexKill   sync.Mutex
+)
+
+// KillSignal 停止信号,主从节点接收到数据后准备重启
+type KillSignal struct {
+	DropUploadData bool
+	Restart        bool
+}
+
+// KillService 定义要远程调用的类型和方法
+type KillService struct{}
+
+// Kill 杀死自身程序,通过通道实现 方法必须满足RPC规范:函数有两个参数,第一个参数是请求,第二个是响应
+func (m *KillService) Kill(args KillSignal, reply *int) error {
+	c_log.GlobalLogger.Info("接收到自杀信号:", args)
+	// 1 杀死 rosbag record 命令
+	ChannelKillRosRecord <- 1
+	// 2 杀死所有 ros 订阅者(时间窗口生辰这)
+	ChannelKillWindowProducer <- 1
+	if args.DropUploadData == true {
+		// 3-1 等待上传结束再杀死
+		ChannelKillMove <- 1
+		ChannelKillConsume <- 1
+	} else {
+		// 3-2 直接杀死
+		ChannelKillMove <- 2
+		ChannelKillConsume <- 2
+	}
+	go killDone(args.Restart)
+	return nil
+}
+func WaitKillSelf() {
+	killService := new(KillService)
+	if err := rpc.Register(killService); err != nil {
+		c_log.GlobalLogger.Error("注册rpc服务失败:", err)
+		return
+	}
+
+	// 等待并处理远程调用请求
+	for {
+		conn, err := config.KillSignalListener.Accept()
+		if err != nil {
+			continue
+		}
+		go rpc.ServeConn(conn)
+	}
+}
+
+func AddKillTimes(info string) {
+	MutexKill.Lock()
+	defer MutexKill.Unlock()
+	switch info {
+	case "1":
+		close(ChannelKillRosRecord)
+		KillTimes++
+		c_log.GlobalLogger.Infof("已杀死record打包goroutine,当前自杀进度 %v / %v", KillTimes, KillChannel)
+		ChannelKillDiskClean <- 1
+	case "2":
+		close(ChannelKillDiskClean)
+		KillTimes++
+		c_log.GlobalLogger.Infof("已杀死bag包数量维护goroutine,当前自杀进度 %v / %v", KillTimes, KillChannel)
+	case "3":
+		close(ChannelKillWindowProducer)
+		KillTimes++
+		c_log.GlobalLogger.Infof("已杀死时间窗口生产者,当前自杀进度 %v / %v", KillTimes, KillChannel)
+	case "4":
+		close(ChannelKillMove)
+		KillTimes++
+		c_log.GlobalLogger.Infof("已杀死bag包移动goroutine,当前自杀进度 %v / %v", KillTimes, KillChannel)
+	case "5":
+		close(ChannelKillConsume)
+		KillTimes++
+		c_log.GlobalLogger.Infof("已杀死bag包消费goroutine,当前自杀进度 %v / %v", KillTimes, KillChannel)
+	}
+}
+
+func killDone(restart bool) {
+	for {
+		time.Sleep(time.Duration(1) * time.Second)
+		if KillChannel == KillTimes {
+			if restart {
+				_, err := util.ExecuteWithPath(config.LocalConfig.RestartCmd.Dir, config.LocalConfig.RestartCmd.Name, config.LocalConfig.RestartCmd.Args...)
+				if err != nil {
+					c_log.GlobalLogger.Info("启动新程序失败,【path】=", config.LocalConfig.RestartCmd.Dir, "【cmd】=", config.LocalConfig.RestartCmd.Name, config.LocalConfig.RestartCmd.Args, ":", err)
+					os.Exit(-1)
+				}
+				c_log.GlobalLogger.Info("数据采集任务更新,正常退出当前程序。")
+			} else {
+				c_log.GlobalLogger.Info("数据采集任务终止,正常退出当前程序。")
+			}
+			os.Exit(0)
+		}
+	}
+}

+ 37 - 0
aarch64/geely/common/service/rosbag_clean.go

@@ -0,0 +1,37 @@
+package service
+
+import (
+	"cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"time"
+)
+
+// BagCacheClean 保证本地缓存的包数量不超过设定值
+func BagCacheClean() {
+	c_log.GlobalLogger.Info("启动清理缓存的 goroutine 维护目录【", config.CloudConfig.BagDataDir, "】的 bag 包数量:", config.CloudConfig.BagNumber)
+	for {
+		// 收到自杀信号
+		select {
+		case signal := <-ChannelKillDiskClean:
+			c_log.GlobalLogger.Info("data目录bag清理线程接收到自杀信号")
+			if signal == 1 {
+				AddKillTimes("2")
+				return
+			}
+		default:
+		}
+
+		// 1 ------- 每10秒清理一次 -------
+		time.Sleep(time.Duration(10) * time.Second)
+		// 2 ------- 获取目录下所有bag包 -------
+		bags, _ := util.ListAbsolutePathWithSuffixAndSort(config.CloudConfig.BagDataDir, ".bag")
+		// 3 如果打包数量超过n个,删除最旧的包{
+		if len(bags) > config.CloudConfig.BagNumber {
+			diff := len(bags) - config.CloudConfig.BagNumber
+			for i := 0; i < diff; i++ {
+				util.DeleteFile(bags[i])
+			}
+		}
+	}
+}

+ 134 - 0
aarch64/geely/common/service/rosbag_record.go

@@ -0,0 +1,134 @@
+package service
+
+import (
+	"cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"github.com/bluenviron/goroslib/v2"
+	"time"
+)
+
+// 打包rosbag
+func BagRecord(nodeName string) {
+	c_log.GlobalLogger.Info("【rosbag record】 goroutine - 启动")
+	for {
+		c_log.GlobalLogger.Info("校验必需的 ROS NODE 是否全部启动。")
+		canRecord := false
+		for !canRecord {
+			time.Sleep(time.Duration(2) * time.Second)
+			canRecord = isCanRecord(config.RosNode)
+		}
+		c_log.GlobalLogger.Info("ROS NODE 启动完成,正在启动record命令。")
+
+		var command []string
+		command = append(command, "record")
+		command = append(command, "--split")
+		command = append(command, "--duration=1")
+		for _, host := range config.CloudConfig.Hosts {
+			if host.Name == nodeName {
+				// 配置文件中的是node1和node2各自的所有topic,platformConfig中配置用户想缓存的topic
+				// 如果有网,配置文件中的是默认要采集的(如果平台没有填,则取默认)
+				if config.LocalConfig.Internet {
+					platformTopics := config.RecordTopics
+					// platformConfig中配置用户想缓存的topic,配置文件中的是默认要采集的(如果平台没有填,则取默认),取交集
+					if platformTopics == nil || len(platformTopics) == 0 {
+						c_log.GlobalLogger.Infof("平台没有配置采集话题【%v】,采集默认话题", platformTopics)
+						for _, topic2 := range host.Topics {
+							command = append(command, topic2)
+						}
+					} else {
+						c_log.GlobalLogger.Infof("平台配置了采集话题【%v】,采集配置话题", platformTopics)
+						for _, topic1 := range config.RecordTopics {
+							command = append(command, topic1)
+						}
+					}
+				} else {
+					for _, topic2 := range host.Topics {
+						command = append(command, topic2)
+					}
+				}
+
+			}
+		}
+
+		// 2 ------- 调用 rosbag 打包命令,该命令自动阻塞 -------
+		// 不在此处压缩,因为 rosbag filter 时会报错。在上传到oss之前压缩即可。
+		// 包名格式:2023-11-15-17-35-20_0.bag
+		util.CreateDir(config.CloudConfig.BagDataDir)
+		//systemEnv := os.Environ()
+		//c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
+		//c_log.GlobalLogger.Info("record环境变量为:", config.RosbagEnvs)
+		cmd, err := util.ExecuteWithEnvAndDirAsync(config.RosbagEnvs, config.CloudConfig.BagDataDir, config.RosbagPath, command...)
+		if err != nil {
+			c_log.GlobalLogger.Error("执行record命令", command, "出错:", err)
+			continue
+		}
+		c_log.GlobalLogger.Infof("启动record命令【%v】成功。", cmd)
+
+		recordProcessPid := cmd.Process.Pid
+		var recordSubProcessPid int
+		for {
+			time.Sleep(time.Duration(2) * time.Second)
+			recordSubProcessPid, err = util.GetSubProcessPid(recordProcessPid)
+			if err != nil {
+				c_log.GlobalLogger.Info("正在等待获取进程 ", recordProcessPid, " 的子进程的pid。(未获取到可能由于磁盘空间已满)")
+				continue
+			}
+			if recordSubProcessPid != 0 {
+				c_log.GlobalLogger.Info("获取进程 ", recordProcessPid, " 的子进程的pid:", recordSubProcessPid)
+				break
+			}
+		}
+		// 等待自杀信号
+		c_log.GlobalLogger.Info("rosbag record goroutine - 等待自杀信号")
+		select {
+		case signal := <-ChannelKillRosRecord:
+			if signal == 1 {
+				if err = util.KillProcessByPid(recordSubProcessPid); err != nil {
+					c_log.GlobalLogger.Errorf("程序阻塞,杀死record命令子进程出错,【pid】=%v,【err】=%v。", recordSubProcessPid, err)
+					select {} // 此处阻塞防止record命令一直录包占满存储
+				}
+				if err = cmd.Process.Kill(); err != nil {
+					c_log.GlobalLogger.Error("程序阻塞,杀死record命令父进程", recordProcessPid, "出错:", err)
+					select {} // 此处阻塞防止record命令一直录包占满存储
+				}
+				AddKillTimes("1")
+				return
+			}
+		}
+
+		// TODO 暂时不放开该逻辑。如果监控rosnode来判断是否杀死record,太麻烦,生成窗口的线程也需要关闭
+		//{
+		//	commonCfg.GlobalLogger.Info("正在监控rosnode是否全部关闭。")
+		//	for canRecord {
+		//		time.Sleep(time.Duration(1) * time.Second)
+		//		canRecord = isCanRecord(commonCfg.RosNode)
+		//	}
+		//	commonCfg.GlobalLogger.Info("rosnode已全部关闭,正在结束record进程。")
+		//
+		//	err = cmd.Process.Kill()
+		//	if err != nil {
+		//		commonCfg.GlobalLogger.Error("杀死record进程错误:", err)
+		//		continue
+		//	}
+		//}
+	}
+}
+
+func isCanRecord(n *goroslib.Node) bool {
+	time.Sleep(time.Duration(1) * time.Second)
+	nodes, err := n.MasterGetNodes()
+	if err != nil {
+		c_log.GlobalLogger.Error("获取rosnode出错:", err)
+		return false
+	}
+	myMap := nodes
+	mySlice := config.CloudConfig.Ros.Nodes
+	for _, element := range mySlice {
+		if _, ok := myMap[element]; !ok {
+			c_log.GlobalLogger.Info("rosnode:", element, " 未启动,需等待启动后才可启动record。")
+			return false
+		}
+	}
+	return true
+}

+ 166 - 0
aarch64/geely/common/service/rosbag_upload.go

@@ -0,0 +1,166 @@
+package service
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	masterConfig "cicv-data-closedloop/aarch64/pjisuv/master/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/domain"
+	"cicv-data-closedloop/common/entity"
+	commonUtil "cicv-data-closedloop/common/util"
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// RunTimeWindowConsumerQueue 依次上传时间窗口
+func RunTimeWindowConsumerQueue() {
+
+	//nodeName:= commonConfig.LocalConfig.Node.Name
+	c_log.GlobalLogger.Info("处理消费者队列goroutine - 启动")
+outLoop:
+	for { // 串行处理
+		// 收到自杀信号
+		select {
+		case signal := <-ChannelKillConsume:
+			c_log.GlobalLogger.Info("消费者队列接收到自杀信号:", signal)
+			if signal == 1 {
+				ChannelKillConsume <- 1
+				if len(entity.TimeWindowConsumerQueue) == 0 {
+					AddKillTimes("5")
+					return
+				}
+			} else { //signal == 2
+				AddKillTimes("5")
+				return
+			}
+		default:
+		}
+		time.Sleep(time.Duration(1) * time.Second)
+		waitLength := len(entity.TimeWindowConsumerQueue)
+		if waitLength == 0 {
+			continue outLoop
+		}
+		// 1 获取即将处理的窗口
+		currentTimeWindow := entity.TimeWindowConsumerQueue[0]
+		entity.RemoveHeadOfTimeWindowConsumerQueue()
+		c_log.GlobalLogger.Infof("开始处理窗口,【Lable】=%v,【FaultTime】=%v,【Length】=%v", currentTimeWindow.Labels, currentTimeWindow.FaultTime, currentTimeWindow.Length)
+
+		// 2 获取目录和bags
+		dir := domain.GetCopyDir(commonConfig.CloudConfig.BagCopyDir, currentTimeWindow.FaultTime)
+		//bags, _ := commonUtil.ListAbsolutePathWithSuffixAndSort(dir, ".bag")
+		// 3 如果不是全量采集,则使用 filter 命令对 bag 包进行主题过滤。
+		// todo  需要采集回传的话题是根据触发器设置的,触发器设置了之后进行过滤
+		//if commonConfig.CloudConfig.FullCollect == false {
+		//	filterTopics := commonConfig.CollectTopics
+		//	if nodeName == commonConfig.CloudConfig.Hosts[0].Name {
+		//		filterTopics = currentTimeWindow.MasterTopics
+		//	} else {
+		//		filterTopics = currentTimeWindow.SlaveTopics
+		//	}
+		//	var topicsFilterSlice []string
+		//	for _, topic := range filterTopics {
+		//		topicsFilterSlice = append(topicsFilterSlice, "topic=='"+topic+"'")
+		//	}
+		//	for i, bag := range bags {
+		//		oldName := bag
+		//		newName := bag + "_filter"
+		//		filterCommand := []string{"filter", oldName, newName, "\"" + strings.Join(topicsFilterSlice, " or ") + "\""}
+		//		_, output, err := commonUtil.ExecuteWithEnvSync(commonConfig.RosbagEnvs, commonConfig.RosbagPath, filterCommand...)
+		//		c_log.GlobalLogger.Info("正在过滤中,【FaultTime】=", currentTimeWindow.FaultTime, "【Label】=", currentTimeWindow.Labels, ",进度", i+1, "/", bagNumber, "。")
+		//		if err != nil {
+		//			c_log.GlobalLogger.Errorf("filter 命令执行出错【命令】=%v,【输出】=%v,【err】=%v", filterCommand, output, err)
+		//			continue
+		//		}
+		//		// 删除旧文件
+		//		_ = commonUtil.DeleteFile(oldName)
+		//		// 将新文件改回旧文件名
+		//		if err = os.Rename(newName, oldName); err != nil {
+		//			c_log.GlobalLogger.Info("修改文件名", oldName, "失败,放弃当前时间窗口", currentTimeWindow.FaultTime, ",错误为:", err)
+		//			continue outLoop
+		//		}
+		//	}
+		//}
+		//
+		//if commonConfig.CloudConfig.CompressBag  {
+		//	// 4 compress包,必须顺序执行,此时每个包会对应生成一个压缩过的包和原始包,原始包后缀为.orig.bag
+		//	c_log.GlobalLogger.Info("压缩bag数据包,故障时间为:", currentTimeWindow.FaultTime)
+		//	for i, bag := range bags {
+		//		oldName := bag
+		//		compressCommand := []string{"compress", "--bz2", oldName}
+		//		c_log.GlobalLogger.Info("正在压缩中,【FaultTime】=", currentTimeWindow.FaultTime, "【Label】=", currentTimeWindow.Labels, ",进度", i+1, "/", bagNumber, "。")
+		//		if _, output, err := commonUtil.ExecuteWithEnvSync(commonConfig.RosbagEnvs, commonConfig.RosbagPath, compressCommand...); err != nil {
+		//			c_log.GlobalLogger.Errorf("compress 命令执行出错【命令】=%v,【输出】=%v,【err】=%v", compressCommand, output, err)
+		//			continue
+		//		}
+		//	}
+		//}
+
+		// 5 upload,必须顺序执行
+		//c_log.GlobalLogger.Info("发送bag数据包,故障时间为:", currentTimeWindow.FaultTime)
+		//start := time.Now()
+		//objectKey1 := commonConfig.OssEquBasePrefix + "data/" + nodeName + "_" + currentTimeWindow.FaultTime + "_" + strings.Join(currentTimeWindow.Labels, "_") + "_" + fmt.Sprintf("%d", bagNumber) + "/"
+		objectKey2 := commonConfig.OssEquBasePrefix + "data_merge/" + currentTimeWindow.FaultTime + "_" + strings.Join(currentTimeWindow.Labels, "_") + "_" + fmt.Sprintf("%d", currentTimeWindow.Length) + ".bag"
+		objectKey3 := commonConfig.OssEquBasePrefix + "data_parse/" + currentTimeWindow.FaultTime + "_" + strings.Join(currentTimeWindow.Labels, "_") + "_" + fmt.Sprintf("%d", currentTimeWindow.Length) + "/"
+		//for i, bag := range bags {
+		//	startOne := time.Now()
+		//	bagSlice := strings.Split(bag, "/")
+		//	for {
+		//		{
+		//			commonConfig.OssMutex.Lock()
+		//			err := commonConfig.OssBucket.PutObjectFromFile(objectKey1+bagSlice[len(bagSlice)-1], bag)
+		//			commonConfig.OssMutex.Unlock()
+		//			if err != nil {
+		//				c_log.GlobalLogger.Info("因网络原因上传包 ", bag, " 时报错,需要等待网络恢复后重新上传:", err)
+		//				continue
+		//			}
+		//		}
+		//		c_log.GlobalLogger.Info("上传耗时 ", time.Since(startOne), ",【FaultTime】=", currentTimeWindow.FaultTime, "【Label】=", currentTimeWindow.Labels, ",进度", i+1, "/", bagNumber, "。【", bag, "】-------【", objectKey1+bagSlice[len(bagSlice)-1], "】")
+		//		break
+		//	}
+		//}
+		//c_log.GlobalLogger.Info("上传完成,总耗时:", time.Since(start))
+		if commonConfig.LocalConfig.Node.Name == "node1" {
+			// 在上传完成的包目录同级下添加一个目录同名的json
+			var triggerIds []string
+			for _, label := range currentTimeWindow.Labels {
+
+				if triggerId, ok := masterConfig.LabelMapTriggerId.Load(label); !ok {
+					c_log.GlobalLogger.Errorf("【label】=%v 没有对应的【triggerId】", label)
+				} else {
+					//c_log.GlobalLogger.Info("添加一个【triggerId】=", triggerId)
+					triggerIds = append(triggerIds, commonUtil.ToString(triggerId))
+				}
+			}
+			//c_log.GlobalLogger.Info("json 中添加【triggerIds】=", triggerIds)
+			callBackJsonBytes, _ := json.MarshalIndent(&entity.CallBack{
+				DataName:    currentTimeWindow.FaultTime,
+				DataSize:    "", // 由合并程序补充
+				EquipmentNo: commonConfig.LocalConfig.EquipmentNo,
+				SecretKey:   commonConfig.LocalConfig.SecretKey,
+				RosBagPath:  objectKey2,
+				FilePath:    objectKey3,
+				TaskId:      commonConfig.PlatformConfig.TaskConfigId,
+				TriggerId:   triggerIds,
+			}, "", "    ")
+			callBackJson := string(callBackJsonBytes)
+			c_log.GlobalLogger.Info("【callBackJson】=", callBackJson)
+
+			{
+				//commonConfig.OssMutex.Lock()
+				//err := commonConfig.OssBucket.PutObject(objectKey3+"callback.json", strings.NewReader(callBackJson))
+				//commonConfig.OssMutex.Unlock()
+				//if err != nil {
+				//	c_log.GlobalLogger.Error("上传 callback.json", callBackJson, "失败:", err)
+				//}
+				// 删除本地所有已上传的bag文件
+				//if err := commonUtil.RemoveDir(dir); err != nil {
+				//	continue outLoop
+				//}
+				commonUtil.WriteStringToFile(callBackJson, dir+"callback.json")
+			}
+		}
+		c_log.GlobalLogger.Infof("结束处理窗口,【Label】=%v,【FaultTime】=%v,【Length】=%v", currentTimeWindow.Labels, currentTimeWindow.FaultTime, currentTimeWindow.Length)
+		c_log.GlobalLogger.Infof("待处理窗口个数为:%v", len(entity.TimeWindowConsumerQueue))
+	}
+}

+ 120 - 0
aarch64/geely/control/main.go

@@ -0,0 +1,120 @@
+package main
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	commonService "cicv-data-closedloop/aarch64/pjisuv/common/service"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"net/rpc"
+	"os"
+	"runtime"
+	"time"
+)
+
+func init() {
+	runtime.GOMAXPROCS(1)
+	// 初始化日志配置
+	c_log.InitLog(commonConfig.LogDir, commonConfig.ControlLogPrefix)
+	//c_log.InitLog("/userdata/cicv-data-closedloop/log/", "pjisuv-control")
+	// 初始化本地配置文件(第1处配置,在本地文件)
+	commonConfig.InitLocalConfig()
+	// 初始化Oss连接信息
+	commonConfig.InitOssConfig()
+	// 初始化业务逻辑配置信息,配置文件在oss上(第2处配置,在oss文件)
+	commonConfig.InitCloudConfig()
+}
+
+func main() {
+	if commonConfig.LocalConfig.Internet {
+		RunWithInternet()
+	} else {
+		RunWithoutInternet()
+	}
+}
+
+func RunWithInternet() {
+	lastStatus := "NONE"
+	wait := false
+	//  轮询任务接口判断是否有更新
+	for {
+		if wait { // 第一次就不等待一分钟了
+			time.Sleep(time.Duration(60) * time.Second)
+		}
+		wait = true
+		// 1 获取当前设备的任务的 status
+		status, err := commonConfig.GetStatus(commonConfig.PlatformConfig.TaskConfigId)
+		if err != nil {
+			c_log.GlobalLogger.Error("获取配置status失败:", err)
+			continue
+		}
+		c_log.GlobalLogger.Info("【lastStatus】=", lastStatus, ",【status】=", status)
+		// 2 判断 status
+		// UN_CHANGE 没有新的任务,无需更改
+		// CHANGE 有新的任务,需要杀死旧的任务并重启
+		// NONE 设备没有配置任务,需要杀死旧的任务
+		if status == "UN_CHANGE" {
+			lastStatus = "UN_CHANGE"
+			continue
+		} else if status == "CHANGE" || status == "NONE" {
+			if lastStatus == "CHANGE" && status == "CHANGE" { // 供更新使用
+				commonConfig.InitPlatformConfig()
+				continue
+			}
+			if lastStatus == "NONE" && status == "NONE" {
+				continue
+			}
+			// 3 发送rpc信号杀死两个服务,并重启程序
+			if lastStatus == "NONE" && status == "CHANGE" {
+				startMasterOrSlave()
+				lastStatus = status
+				c_log.GlobalLogger.Info("获取数据闭环平台最新配置。")
+				commonConfig.InitPlatformConfig()
+				continue
+			}
+			var killArgs commonService.KillSignal
+			if lastStatus == "UN_CHANGE" && status == "CHANGE" {
+				killArgs = commonService.KillSignal{DropUploadData: commonConfig.PlatformConfig.DropUploadData, Restart: true}
+				c_log.GlobalLogger.Info("更新任务,发送rpc重启信号到本地:", killArgs)
+			}
+			if lastStatus == "UN_CHANGE" && status == "NONE" {
+				killArgs = commonService.KillSignal{DropUploadData: commonConfig.PlatformConfig.DropUploadData, Restart: false}
+				c_log.GlobalLogger.Info("杀死任务,发送rpc结束信号到本地:", killArgs)
+			}
+
+			KillRpcClient, err := rpc.Dial("tcp", commonConfig.LocalConfig.Node.Ip+":"+commonConfig.CloudConfig.RpcPort)
+			if err != nil {
+				// 此处如果连接失败说明采集程序已经停止了
+				lastStatus = "NONE"
+				c_log.GlobalLogger.Error("采集程序已经停止:", err)
+				continue
+			}
+
+			reply := 0
+			if err = KillRpcClient.Call("KillService.Kill", killArgs, &reply); err != nil {
+				c_log.GlobalLogger.Error("发送rpc请求到master失败:", err)
+				// 这里可能会报错 unexpected EOF 但是不影响,先注释 close 和 continue
+				//KillRpcClient.Close()
+				//continue
+			}
+			lastStatus = status
+			c_log.GlobalLogger.Info("结束任务后,将数据闭环平台配置置空。")
+			commonConfig.PlatformConfig = commonConfig.PlatformConfigStruct{}
+			if err = KillRpcClient.Close(); err != nil {
+				// 不做处理
+			}
+		} else {
+			c_log.GlobalLogger.Error("未知的采集任务状态。【status】=", status)
+		}
+	}
+}
+func RunWithoutInternet() {
+	startMasterOrSlave()
+}
+
+func startMasterOrSlave() {
+	if _, err := util.ExecuteWithPath(commonConfig.LocalConfig.RestartCmd.Dir, commonConfig.LocalConfig.RestartCmd.Name, commonConfig.LocalConfig.RestartCmd.Args...); err != nil {
+		c_log.GlobalLogger.Info("启动新程序失败,【path】=", commonConfig.LocalConfig.RestartCmd.Dir, "【cmd】=", commonConfig.LocalConfig.RestartCmd.Name, commonConfig.LocalConfig.RestartCmd.Args, ":", err)
+		os.Exit(-1)
+	}
+	c_log.GlobalLogger.Info("启动任务,本地执行启动命令:【path】=", commonConfig.LocalConfig.RestartCmd.Dir, "【cmd】=", commonConfig.LocalConfig.RestartCmd.Name, commonConfig.LocalConfig.RestartCmd.Args)
+}

+ 61 - 0
aarch64/geely/master/config/c_local.go

@@ -0,0 +1,61 @@
+package config
+
+import (
+	"cicv-data-closedloop/common/config/c_log"
+	_ "embed"
+	"gopkg.in/yaml.v2"
+	"os"
+)
+
+var (
+	//go:embed local-config.yaml
+	localConfigString string
+
+	LocalConfig     localConfig
+	localConfigPath = "/mnt/media/sda1/cicv-data-closedloop/config/local-config.yaml"
+	//localConfigPath = "/userdata/cicv-data-closedloop/config/local-config.yaml"
+	OssEquBasePrefix string
+)
+
+type node struct {
+	Name string `yaml:"name"`
+	Ip   string `yaml:"ip"`
+}
+
+type restartCmd struct {
+	Dir  string   `yaml:"dir"`
+	Name string   `yaml:"name"`
+	Args []string `yaml:"args"`
+}
+
+type localConfig struct {
+	Internet             bool       `yaml:"internet"`
+	Node                 node       `yaml:"node"`                    // 获取oss配置的url
+	UrlGetOssConfig      string     `yaml:"url-get-oss-config"`      // 获取oss配置的url
+	OssBasePrefix        string     `yaml:"oss-base-prefix"`         // 云端配置文件的位置
+	EquipmentNo          string     `yaml:"equipment-no"`            // 当前设备的编号
+	CloudConfigFilename  string     `yaml:"cloud-config-filename"`   // 云端配置文件名称
+	CloudConfigLocalPath string     `yaml:"cloud-config-local-path"` // 将 oss 的配置文件下载到本地的位置
+	SecretKey            string     `yaml:"secret-key"`              // 当前设备的密钥
+	RestartCmd           restartCmd `yaml:"restart-cmd"`             // 重启命令
+}
+
+func InitLocalConfig() {
+	c_log.GlobalLogger.Info("初始化本地配置文件 - 开始:", localConfigPath)
+	// 读取YAML文件内容
+	content, err := os.ReadFile(localConfigPath)
+	if err != nil {
+		c_log.GlobalLogger.Error("读取本地配置文件失败。", err)
+		os.Exit(-1)
+	}
+
+	// 解析YAML内容
+	err = yaml.Unmarshal(content, &LocalConfig)
+	if err != nil {
+		c_log.GlobalLogger.Error("解析本地配置文件失败。", err)
+		os.Exit(-1)
+	}
+
+	c_log.GlobalLogger.Info("初始化本地配置文件 - 成功:", LocalConfig)
+	OssEquBasePrefix = LocalConfig.OssBasePrefix + LocalConfig.EquipmentNo + "/"
+}

+ 27 - 0
aarch64/geely/master/config/local-config.yaml

@@ -0,0 +1,27 @@
+log:
+  dir: "/mnt/media/sda1/cicv-data-closedloop/log/"
+  prefix:
+
+
+# 是否联通互联网
+internet: false
+# 节点名称和IP
+node:
+  name: node1
+  ip: 192.168.1.104
+# 数据闭环平台参数
+equipment-no: kinglong-003
+secret-key: ri6uhgae
+# 获取oss连接信息的接口url
+url-get-oss-config: http://36.110.106.156:18379/oss/config?token=nXonLUcMtGcrQqqKiyygIwyVbvizE0wD
+# 金龙车数据前缀
+oss-base-prefix: kinglong/
+# oss上的配置文件的名称
+cloud-config-filename: cloud-config.yaml
+# 将oss上的配置文件下载到本地的路径
+cloud-config-local-path: /mnt/media/sda1/cicv-data-closedloop/config/cloud-config.yaml
+restart-cmd:
+  dir: "/mnt/media/sda1/cicv-data-closedloop/"
+  name: "sh"
+  args:
+    - "start-soc1.sh"

+ 547 - 0
aarch64/geely/master/config/trigger_init.go

@@ -0,0 +1,547 @@
+package config
+
+import (
+	"cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"cicv-data-closedloop/pjisuv_msgs"
+	"cicv-data-closedloop/pjisuv_ticker"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/geometry_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/nav_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/sensor_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/tf2_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/visualization_msgs"
+	"plugin"
+	"slices"
+	"sync"
+)
+
+func InitTriggerConfig() {
+	config.OssMutex.Lock()
+	defer config.OssMutex.Unlock()
+	triggerLocalPathsMapTriggerId := make(map[string]string)
+	c_log.GlobalLogger.Info("主节点加载触发器插件 - 开始。")
+	if config.LocalConfig.Internet {
+		// 1 获取数采任务的触发器列表
+		cloudTriggers := &config.PlatformConfig.TaskTriggers
+		// 2 获取本地触发器列表(触发器目录的一级子目录为【触发器ID_触发器Label】)
+		localTriggerIds := util.GetFirstLevelSubdirectories(config.CloudConfig.TriggersDir)
+		// 3 对比触发器列表,本地没有的则下载
+		for _, trigger := range *cloudTriggers {
+			id := util.ToString(trigger.TriggerId)
+			hasIdDir := slices.Contains(localTriggerIds, id) // 改成了 slices 工具库
+			triggerLocalDir := config.CloudConfig.TriggersDir + id + "/"
+			hasLabelSo, soPaths := util.CheckSoFilesInDirectory(triggerLocalDir)
+			var triggerLocalPath string
+			if hasIdDir && hasLabelSo { // 已存在的触发器需要判断是否大小一致
+				triggerLocalPath = soPaths[0]
+				ossSize, _ := util.GetOSSFileSize(config.OssBucket, trigger.TriggerScriptPath)
+				localSize, _ := util.GetFileSize(triggerLocalPath)
+				if ossSize == localSize {
+					c_log.GlobalLogger.Info("触发器插件 ", triggerLocalPath, " 存在且与云端触发器大小一致。")
+					triggerLocalPathsMapTriggerId[triggerLocalPath] = id
+					continue
+				}
+			}
+			label := util.GetFileNameWithoutExtension(config.CloudConfig.TriggersDir + trigger.TriggerScriptPath)
+			triggerLocalPath = config.CloudConfig.TriggersDir + id + "/" + label + ".so"
+			c_log.GlobalLogger.Info("开始下载触发器插件从 ", trigger.TriggerScriptPath, " 到 ", triggerLocalPath)
+			_ = util.CreateParentDir(triggerLocalPath)
+			for {
+				if err := config.OssBucket.GetObjectToFile(trigger.TriggerScriptPath, triggerLocalPath); err != nil {
+					c_log.GlobalLogger.Error("下载触发器插件失败,再次尝试:", err)
+					continue
+				}
+				break
+			}
+			triggerLocalPathsMapTriggerId[triggerLocalPath] = id
+		}
+	} else {
+		errorCodeSoPath := "/mnt/media/sda1/cicv-data-closedloop/ErrorCode.so"
+		c_log.GlobalLogger.Info("无法连接互联网,提前准备触发器插件文件:", errorCodeSoPath)
+		triggerLocalPathsMapTriggerId[errorCodeSoPath] = "ErrorCodeId"
+	}
+
+	success := 0
+	// 加载所有触发器的文件
+	for triggerLocalPath, triggerId := range triggerLocalPathsMapTriggerId {
+		// 载入插件到数组
+		open, err := plugin.Open(triggerLocalPath)
+		if err != nil {
+			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "失败。", err)
+			continue
+		}
+		topic0, err := open.Lookup("Topic")
+		if err != nil {
+			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "中的Topic方法失败。", err)
+			continue
+		}
+		topic1, ok := topic0.(func() string)
+		if ok != true {
+			c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func() string):", err)
+			continue
+		}
+		topic2 := topic1()
+		rule, err := open.Lookup("Rule")
+		if err != nil {
+			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "中的Rule方法失败。", err)
+			continue
+		}
+
+		if pjisuv_ticker.TickerTopic == topic2 { // 定时任务触发器
+			if f, ok1 := rule.(func(shareVars *sync.Map)); ok1 {
+				RuleOfCicvTicker = append(RuleOfCicvTicker, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfAmrPose == topic2 { //1
+			if f, ok1 := rule.(func(data *visualization_msgs.MarkerArray) string); ok1 {
+				RuleOfAmrPose1 = append(RuleOfAmrPose1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *visualization_msgs.MarkerArray) string); ok3 {
+				RuleOfAmrPose3 = append(RuleOfAmrPose3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfBoundingBoxesFast == topic2 { //2
+			if f, ok1 := rule.(func(data *pjisuv_msgs.BoundingBoxArray) string); ok1 {
+				RuleOfBoundingBoxesFast1 = append(RuleOfBoundingBoxesFast1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.BoundingBoxArray) string); ok3 {
+				RuleOfBoundingBoxesFast3 = append(RuleOfBoundingBoxesFast3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCameraFault == topic2 { //3
+			if f, ok1 := rule.(func(data *pjisuv_msgs.FaultVec) string); ok1 {
+				RuleOfCameraFault1 = append(RuleOfCameraFault1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.FaultVec) string); ok3 {
+				RuleOfCameraFault3 = append(RuleOfCameraFault3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCanData == topic2 { //4
+			if f, ok1 := rule.(func(data *pjisuv_msgs.Frame) string); ok1 {
+				RuleOfCanData1 = append(RuleOfCanData1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.Frame) string); ok3 {
+				RuleOfCanData3 = append(RuleOfCanData3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCh128x1LslidarPointCloud == topic2 { //5
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfCh128x1LslidarPointCloud1 = append(RuleOfCh128x1LslidarPointCloud1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfCh128x1LslidarPointCloud3 = append(RuleOfCh128x1LslidarPointCloud3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCh64wLLslidarPointCloud == topic2 { //6
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfCh64wLLslidarPointCloud1 = append(RuleOfCh64wLLslidarPointCloud1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfCh64wLLslidarPointCloud3 = append(RuleOfCh64wLLslidarPointCloud3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCh64wLScan == topic2 { //7
+			if f, ok1 := rule.(func(data *sensor_msgs.LaserScan) string); ok1 {
+				RuleOfCh64wLScan1 = append(RuleOfCh64wLScan1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.LaserScan) string); ok3 {
+				RuleOfCh64wLScan3 = append(RuleOfCh64wLScan3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCh64wRLslidarPointCloud == topic2 { //8
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfCh64wRLslidarPointCloud1 = append(RuleOfCh64wRLslidarPointCloud1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfCh64wRLslidarPointCloud3 = append(RuleOfCh64wRLslidarPointCloud3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCh64wRScan == topic2 { //9
+			if f, ok1 := rule.(func(data *sensor_msgs.LaserScan) string); ok1 {
+				RuleOfCh64wRScan1 = append(RuleOfCh64wRScan1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.LaserScan) string); ok3 {
+				RuleOfCh64wRScan3 = append(RuleOfCh64wRScan3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCicvLidarclusterMovingObjects == topic2 { //10
+			if f, ok1 := rule.(func(data *pjisuv_msgs.PerceptionCicvMovingObjects) string); ok1 {
+				RuleOfCicvLidarclusterMovingObjects1 = append(RuleOfCicvLidarclusterMovingObjects1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionCicvMovingObjects) string); ok3 {
+				RuleOfCicvLidarclusterMovingObjects3 = append(RuleOfCicvLidarclusterMovingObjects3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCicvAmrTrajectory == topic2 { //11
+			if f, ok1 := rule.(func(data *pjisuv_msgs.Trajectory) string); ok1 {
+				RuleOfCicvAmrTrajectory1 = append(RuleOfCicvAmrTrajectory1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.Trajectory) string); ok3 {
+				RuleOfCicvAmrTrajectory3 = append(RuleOfCicvAmrTrajectory3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCicvLocation == topic2 { //12
+			if f, ok1 := rule.(func(data *pjisuv_msgs.PerceptionLocalization) string); ok1 {
+				RuleOfCicvLocation1 = append(RuleOfCicvLocation1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string); ok3 {
+				RuleOfCicvLocation3 = append(RuleOfCicvLocation3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCloudClusters == topic2 { //13
+			if f, ok1 := rule.(func(data *pjisuv_msgs.AutowareCloudClusterArray) string); ok1 {
+				RuleOfCloudClusters1 = append(RuleOfCloudClusters1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.AutowareCloudClusterArray) string); ok3 {
+				RuleOfCloudClusters3 = append(RuleOfCloudClusters3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfHeartbeatInfo == topic2 { //14
+			if f, ok1 := rule.(func(data *pjisuv_msgs.HeartBeatInfo) string); ok1 {
+				RuleOfHeartbeatInfo1 = append(RuleOfHeartbeatInfo1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.HeartBeatInfo) string); ok3 {
+				RuleOfHeartbeatInfo3 = append(RuleOfHeartbeatInfo3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfLidarPretreatmentCost == topic2 { //15
+			if f, ok1 := rule.(func(data *geometry_msgs.Vector3Stamped) string); ok1 {
+				RuleOfLidarPretreatmentCost1 = append(RuleOfLidarPretreatmentCost1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *geometry_msgs.Vector3Stamped) string); ok3 {
+				RuleOfLidarPretreatmentCost3 = append(RuleOfLidarPretreatmentCost3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfLidarPretreatmentOdometry == topic2 { //16
+			if f, ok1 := rule.(func(data *nav_msgs.Odometry) string); ok1 {
+				RuleOfLidarPretreatmentOdometry1 = append(RuleOfLidarPretreatmentOdometry1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *nav_msgs.Odometry) string); ok3 {
+				RuleOfLidarPretreatmentOdometry3 = append(RuleOfLidarPretreatmentOdometry3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfLidarRoi == topic2 { //17
+			if f, ok1 := rule.(func(data *geometry_msgs.PolygonStamped) string); ok1 {
+				RuleOfLidarRoi1 = append(RuleOfLidarRoi1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *geometry_msgs.PolygonStamped) string); ok3 {
+				RuleOfLidarRoi3 = append(RuleOfLidarRoi3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfLine1 == topic2 { //18
+			if f, ok1 := rule.(func(data *nav_msgs.Path) string); ok1 {
+				RuleOfLine11 = append(RuleOfLine11, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *nav_msgs.Path) string); ok3 {
+				RuleOfLine13 = append(RuleOfLine13, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfLine2 == topic2 { //19
+			if f, ok1 := rule.(func(data *nav_msgs.Path) string); ok1 {
+				RuleOfLine21 = append(RuleOfLine21, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *nav_msgs.Path) string); ok3 {
+				RuleOfLine23 = append(RuleOfLine23, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfMapPolygon == topic2 { //20
+			if f, ok1 := rule.(func(data *pjisuv_msgs.PolygonStamped) string); ok1 {
+				RuleOfMapPolygon1 = append(RuleOfMapPolygon1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.PolygonStamped) string); ok3 {
+				RuleOfMapPolygon3 = append(RuleOfMapPolygon3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfObstacleDisplay == topic2 { //21
+			if f, ok1 := rule.(func(data *visualization_msgs.MarkerArray) string); ok1 {
+				RuleOfObstacleDisplay1 = append(RuleOfObstacleDisplay1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *visualization_msgs.MarkerArray) string); ok3 {
+				RuleOfObstacleDisplay3 = append(RuleOfObstacleDisplay3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfPjControlPub == topic2 { //22
+			if f, ok1 := rule.(func(data *pjisuv_msgs.CommonVehicleCmd) string); ok1 {
+				RuleOfPjControlPub1 = append(RuleOfPjControlPub1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.CommonVehicleCmd) string); ok3 {
+				RuleOfPjControlPub3 = append(RuleOfPjControlPub3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfPointsCluster == topic2 { //23
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfPointsCluster1 = append(RuleOfPointsCluster1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfPointsCluster3 = append(RuleOfPointsCluster3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfPointsConcat == topic2 { //24
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfPointsConcat1 = append(RuleOfPointsConcat1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfPointsConcat3 = append(RuleOfPointsConcat3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfReferenceDisplay == topic2 { //25
+			if f, ok1 := rule.(func(data *nav_msgs.Path) string); ok1 {
+				RuleOfReferenceDisplay1 = append(RuleOfReferenceDisplay1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *nav_msgs.Path) string); ok3 {
+				RuleOfReferenceDisplay3 = append(RuleOfReferenceDisplay3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfReferenceTrajectory == topic2 { //26
+			if f, ok1 := rule.(func(data *pjisuv_msgs.Trajectory) string); ok1 {
+				RuleOfReferenceTrajectory1 = append(RuleOfReferenceTrajectory1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.Trajectory) string); ok3 {
+				RuleOfReferenceTrajectory3 = append(RuleOfReferenceTrajectory3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfRoiPoints == topic2 { //27
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfRoiPoints1 = append(RuleOfRoiPoints1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfRoiPoints3 = append(RuleOfRoiPoints3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfRoiPolygon == topic2 { //28
+			if f, ok1 := rule.(func(data *nav_msgs.Path) string); ok1 {
+				RuleOfRoiPolygon1 = append(RuleOfRoiPolygon1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *nav_msgs.Path) string); ok3 {
+				RuleOfRoiPolygon3 = append(RuleOfRoiPolygon3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfTf == topic2 { //29
+			if f, ok1 := rule.(func(data *tf2_msgs.TFMessage) string); ok1 {
+				RuleOfTf1 = append(RuleOfTf1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *tf2_msgs.TFMessage) string); ok3 {
+				RuleOfTf3 = append(RuleOfTf3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfTpperception == topic2 { //30
+			if f, ok1 := rule.(func(data *pjisuv_msgs.PerceptionObjects) string); ok1 {
+				RuleOfTpperception1 = append(RuleOfTpperception1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionObjects) string); ok3 {
+				RuleOfTpperception3 = append(RuleOfTpperception3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfTpperceptionVis == topic2 { //31
+			if f, ok1 := rule.(func(data *visualization_msgs.MarkerArray) string); ok1 {
+				RuleOfTpperceptionVis1 = append(RuleOfTpperceptionVis1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *visualization_msgs.MarkerArray) string); ok3 {
+				RuleOfTpperceptionVis3 = append(RuleOfTpperceptionVis3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfTprouteplan == topic2 { //32
+			if f, ok1 := rule.(func(data *pjisuv_msgs.RoutePlan) string); ok1 {
+				RuleOfTprouteplan1 = append(RuleOfTprouteplan1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.RoutePlan) string); ok3 {
+				RuleOfTprouteplan3 = append(RuleOfTprouteplan3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfTrajectoryDisplay == topic2 { //33
+			if f, ok1 := rule.(func(data *nav_msgs.Path) string); ok1 {
+				RuleOfTrajectoryDisplay1 = append(RuleOfTrajectoryDisplay1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *nav_msgs.Path) string); ok3 {
+				RuleOfTrajectoryDisplay3 = append(RuleOfTrajectoryDisplay3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfUngroundCloudpoints == topic2 { //34
+			if f, ok1 := rule.(func(data *sensor_msgs.PointCloud2) string); ok1 {
+				RuleOfUngroundCloudpoints1 = append(RuleOfUngroundCloudpoints1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string); ok3 {
+				RuleOfUngroundCloudpoints3 = append(RuleOfUngroundCloudpoints3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfCameraImage == topic2 { //35
+			if f, ok1 := rule.(func(data *sensor_msgs.Image) string); ok1 {
+				RuleOfCameraImage1 = append(RuleOfCameraImage1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *sensor_msgs.Image) string); ok3 {
+				RuleOfCameraImage3 = append(RuleOfCameraImage3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfDataRead == topic2 { //36
+			if f, ok1 := rule.(func(data *pjisuv_msgs.Retrieval) string); ok1 {
+				RuleOfDataRead1 = append(RuleOfDataRead1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.Retrieval) string); ok3 {
+				RuleOfDataRead3 = append(RuleOfDataRead3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfPjiGps == topic2 { //37
+			if f, ok1 := rule.(func(data *pjisuv_msgs.PerceptionLocalization) string); ok1 {
+				RuleOfPjiGps1 = append(RuleOfPjiGps1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string); ok3 {
+				RuleOfPjiGps3 = append(RuleOfPjiGps3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfFaultInfo == topic2 { //38
+			if f, ok1 := rule.(func(data *pjisuv_msgs.FaultVec) string); ok1 {
+				RuleOfFaultInfo1 = append(RuleOfFaultInfo1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.FaultVec) string); ok3 {
+				RuleOfFaultInfo3 = append(RuleOfFaultInfo3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfPjVehicleFdbPub == topic2 { //39
+			if f, ok1 := rule.(func(data *pjisuv_msgs.VehicleFdb) string); ok1 {
+				RuleOfPjVehicleFdbPub1 = append(RuleOfPjVehicleFdbPub1, f)
+				goto JudgeDone
+			}
+			if f, ok3 := rule.(func(shareVars *sync.Map, data *pjisuv_msgs.VehicleFdb) string); ok3 {
+				RuleOfPjVehicleFdbPub3 = append(RuleOfPjVehicleFdbPub3, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else {
+			c_log.GlobalLogger.Error("未知的topic:", topic2)
+			continue
+		}
+	JudgeDone:
+		label, err := open.Lookup("Label")
+		if err != nil {
+			c_log.GlobalLogger.Error("加载本地插件 ", triggerLocalPath, " 中的 Label 方法失败。", err)
+			continue
+		}
+		labelFunc := label.(func() string)
+		labelString := labelFunc()
+		LabelMapTriggerId.Store(labelString, triggerId)
+		success++
+	}
+	c_log.GlobalLogger.Info("一共应加载", len(triggerLocalPathsMapTriggerId), "个触发器,实际加载 ", success, " 个触发器。")
+}
+
+func log(triggerLocalPath string) {
+	c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的 Rule 方法参数格式不正确。")
+}

+ 268 - 0
aarch64/geely/master/config/trigger_var.go

@@ -0,0 +1,268 @@
+package config
+
+import (
+	"cicv-data-closedloop/pjisuv_msgs"
+	"cicv-data-closedloop/pjisuv_ticker"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/geometry_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/nav_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/sensor_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/tf2_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/visualization_msgs"
+	"sync"
+)
+
+var (
+	LabelMapTriggerId = new(sync.Map)
+
+	// 0
+	// 定时任务触发器,详见 pjisuv_ticker包
+	TopicOfCicvTicker = pjisuv_ticker.TickerTopic
+	RuleOfCicvTicker  = make([]func(shareVars *sync.Map), 0) // tick代表定时任务间隔时间;对于长度为0的slice,无论是使用var还是make创建,它们在内存占用上的差异通常可以忽略不计
+
+	// 1
+	TopicOfAmrPose = "/amr_pose"
+	RuleOfAmrPose1 []func(data *visualization_msgs.MarkerArray) string
+	RuleOfAmrPose3 []func(shareVars *sync.Map, data *visualization_msgs.MarkerArray) string
+
+	// 2
+	TopicOfBoundingBoxesFast = "/bounding_boxes_fast"
+	RuleOfBoundingBoxesFast1 []func(data *pjisuv_msgs.BoundingBoxArray) string
+	RuleOfBoundingBoxesFast3 []func(shareVars *sync.Map, data *pjisuv_msgs.BoundingBoxArray) string
+
+	// 3
+	TopicOfCameraFault = "/camera_fault"
+	RuleOfCameraFault1 []func(data *pjisuv_msgs.FaultVec) string
+	RuleOfCameraFault3 []func(shareVars *sync.Map, data *pjisuv_msgs.FaultVec) string
+
+	// 4
+	TopicOfCanData = "/can_data"
+	RuleOfCanData1 []func(data *pjisuv_msgs.Frame) string
+	RuleOfCanData3 []func(shareVars *sync.Map, data *pjisuv_msgs.Frame) string
+
+	// 5
+	TopicOfCh128x1LslidarPointCloud = "/ch128x1/lslidar_point_cloud"
+	RuleOfCh128x1LslidarPointCloud1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfCh128x1LslidarPointCloud3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 6
+	TopicOfCh64wLLslidarPointCloud = "/ch64w_l/lslidar_point_cloud"
+	RuleOfCh64wLLslidarPointCloud1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfCh64wLLslidarPointCloud3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 7
+	TopicOfCh64wLScan = "/ch64w_l/scan"
+	RuleOfCh64wLScan1 []func(data *sensor_msgs.LaserScan) string
+	RuleOfCh64wLScan3 []func(shareVars *sync.Map, data *sensor_msgs.LaserScan) string
+
+	// 8
+	TopicOfCh64wRLslidarPointCloud = "/ch64w_r/lslidar_point_cloud"
+	RuleOfCh64wRLslidarPointCloud1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfCh64wRLslidarPointCloud3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 9
+	TopicOfCh64wRScan = "/ch64w_r/scan"
+	RuleOfCh64wRScan1 []func(data *sensor_msgs.LaserScan) string
+	RuleOfCh64wRScan3 []func(shareVars *sync.Map, data *sensor_msgs.LaserScan) string
+
+	// 10
+	TopicOfCicvLidarclusterMovingObjects = "/cicv/lidarcluster_moving_objects"
+	RuleOfCicvLidarclusterMovingObjects1 []func(data *pjisuv_msgs.PerceptionCicvMovingObjects) string
+	RuleOfCicvLidarclusterMovingObjects3 []func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionCicvMovingObjects) string
+
+	// 11
+	TopicOfCicvAmrTrajectory = "/cicv_amr_trajectory"
+	RuleOfCicvAmrTrajectory1 []func(data *pjisuv_msgs.Trajectory) string
+	RuleOfCicvAmrTrajectory3 []func(shareVars *sync.Map, data *pjisuv_msgs.Trajectory) string
+
+	// 12
+	TopicOfCicvLocation = "/cicv_location"
+	RuleOfCicvLocation1 []func(data *pjisuv_msgs.PerceptionLocalization) string
+	RuleOfCicvLocation3 []func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
+
+	// 13
+	TopicOfCloudClusters = "/cloud_clusters"
+	RuleOfCloudClusters1 []func(data *pjisuv_msgs.AutowareCloudClusterArray) string
+	RuleOfCloudClusters3 []func(shareVars *sync.Map, data *pjisuv_msgs.AutowareCloudClusterArray) string
+
+	// 14
+	TopicOfHeartbeatInfo = "/heartbeat_info"
+	RuleOfHeartbeatInfo1 []func(data *pjisuv_msgs.HeartBeatInfo) string
+	RuleOfHeartbeatInfo3 []func(shareVars *sync.Map, data *pjisuv_msgs.HeartBeatInfo) string
+
+	// 15
+	TopicOfLidarPretreatmentCost = "/lidarPretreatment_Cost"
+	RuleOfLidarPretreatmentCost1 []func(data *geometry_msgs.Vector3Stamped) string
+	RuleOfLidarPretreatmentCost3 []func(shareVars *sync.Map, data *geometry_msgs.Vector3Stamped) string
+
+	// 16
+	TopicOfLidarPretreatmentOdometry = "/lidar_pretreatment/odometry"
+	RuleOfLidarPretreatmentOdometry1 []func(data *nav_msgs.Odometry) string
+	RuleOfLidarPretreatmentOdometry3 []func(shareVars *sync.Map, data *nav_msgs.Odometry) string
+
+	// 17
+	TopicOfLidarRoi = "/lidar_roi"
+	RuleOfLidarRoi1 []func(data *geometry_msgs.PolygonStamped) string
+	RuleOfLidarRoi3 []func(shareVars *sync.Map, data *geometry_msgs.PolygonStamped) string
+
+	// 18
+	TopicOfLine1 = "/line_1"
+	RuleOfLine11 []func(data *nav_msgs.Path) string
+	RuleOfLine13 []func(shareVars *sync.Map, data *nav_msgs.Path) string
+
+	// 19
+	TopicOfLine2 = "/line_2"
+	RuleOfLine21 []func(data *nav_msgs.Path) string
+	RuleOfLine23 []func(shareVars *sync.Map, data *nav_msgs.Path) string
+
+	// 20
+	TopicOfMapPolygon = "/map_polygon"
+	RuleOfMapPolygon1 []func(data *pjisuv_msgs.PolygonStamped) string
+	RuleOfMapPolygon3 []func(shareVars *sync.Map, data *pjisuv_msgs.PolygonStamped) string
+
+	// 21
+	TopicOfObstacleDisplay = "/obstacle_display"
+	RuleOfObstacleDisplay1 []func(data *visualization_msgs.MarkerArray) string
+	RuleOfObstacleDisplay3 []func(shareVars *sync.Map, data *visualization_msgs.MarkerArray) string
+
+	// 22
+	TopicOfPjControlPub = "/pj_control_pub"
+	RuleOfPjControlPub1 []func(data *pjisuv_msgs.CommonVehicleCmd) string
+	RuleOfPjControlPub3 []func(shareVars *sync.Map, data *pjisuv_msgs.CommonVehicleCmd) string
+
+	// 23
+	TopicOfPointsCluster = "/points_cluster"
+	RuleOfPointsCluster1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfPointsCluster3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 24
+	TopicOfPointsConcat = "/points_concat"
+	RuleOfPointsConcat1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfPointsConcat3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 25
+	TopicOfReferenceDisplay = "/reference_display"
+	RuleOfReferenceDisplay1 []func(data *nav_msgs.Path) string
+	RuleOfReferenceDisplay3 []func(shareVars *sync.Map, data *nav_msgs.Path) string
+
+	// 26
+	TopicOfReferenceTrajectory = "/reference_trajectory"
+	RuleOfReferenceTrajectory1 []func(data *pjisuv_msgs.Trajectory) string
+	RuleOfReferenceTrajectory3 []func(shareVars *sync.Map, data *pjisuv_msgs.Trajectory) string
+
+	// 27
+	TopicOfRoiPoints = "/roi/points"
+	RuleOfRoiPoints1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfRoiPoints3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 28
+	TopicOfRoiPolygon = "/roi/polygon"
+	RuleOfRoiPolygon1 []func(data *nav_msgs.Path) string
+	RuleOfRoiPolygon3 []func(shareVars *sync.Map, data *nav_msgs.Path) string
+
+	// 29
+	TopicOfTf = "/tf"
+	RuleOfTf1 []func(data *tf2_msgs.TFMessage) string
+	RuleOfTf3 []func(shareVars *sync.Map, data *tf2_msgs.TFMessage) string
+
+	// 30
+	TopicOfTpperception = "/tpperception"
+	RuleOfTpperception1 []func(data *pjisuv_msgs.PerceptionObjects) string
+	RuleOfTpperception3 []func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionObjects) string
+
+	// 31
+	TopicOfTpperceptionVis = "/tpperception/vis"
+	RuleOfTpperceptionVis1 []func(data *visualization_msgs.MarkerArray) string
+	RuleOfTpperceptionVis3 []func(shareVars *sync.Map, data *visualization_msgs.MarkerArray) string
+
+	// 32
+	TopicOfTprouteplan = "/tprouteplan"
+	RuleOfTprouteplan1 []func(data *pjisuv_msgs.RoutePlan) string
+	RuleOfTprouteplan3 []func(shareVars *sync.Map, data *pjisuv_msgs.RoutePlan) string
+
+	// 33
+	TopicOfTrajectoryDisplay = "/trajectory_display"
+	RuleOfTrajectoryDisplay1 []func(data *nav_msgs.Path) string
+	RuleOfTrajectoryDisplay3 []func(shareVars *sync.Map, data *nav_msgs.Path) string
+
+	// 34
+	TopicOfUngroundCloudpoints = "/unground_cloudpoints"
+	RuleOfUngroundCloudpoints1 []func(data *sensor_msgs.PointCloud2) string
+	RuleOfUngroundCloudpoints3 []func(shareVars *sync.Map, data *sensor_msgs.PointCloud2) string
+
+	// 35
+	TopicOfCameraImage = "/camera_image"
+	RuleOfCameraImage1 []func(data *sensor_msgs.Image) string
+	RuleOfCameraImage3 []func(shareVars *sync.Map, data *sensor_msgs.Image) string
+
+	// 36
+	TopicOfDataRead = "/data_read"
+	RuleOfDataRead1 []func(data *pjisuv_msgs.Retrieval) string
+	RuleOfDataRead3 []func(shareVars *sync.Map, data *pjisuv_msgs.Retrieval) string
+
+	// 37
+	TopicOfPjiGps = "/pji_gps"
+	RuleOfPjiGps1 []func(data *pjisuv_msgs.PerceptionLocalization) string
+	RuleOfPjiGps3 []func(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
+
+	// 38
+	TopicOfFaultInfo = "/fault_info"
+	RuleOfFaultInfo1 []func(data *pjisuv_msgs.FaultVec) string
+	RuleOfFaultInfo3 []func(shareVars *sync.Map, data *pjisuv_msgs.FaultVec) string
+
+	//39
+	TopicOfPjVehicleFdbPub = "/pj_vehicle_fdb_pub"
+	RuleOfPjVehicleFdbPub1 []func(data *pjisuv_msgs.VehicleFdb) string
+	RuleOfPjVehicleFdbPub3 []func(shareVars *sync.Map, data *pjisuv_msgs.VehicleFdb) string
+
+	//40
+	TopicOfEndPointMessage = "/end_point_message"
+	RuleOfEndPointMessage1 []func(data *geometry_msgs.Point) string
+	RuleOfEndPointMessage3 []func(shareVars *sync.Map, data *geometry_msgs.Point) string
+
+	// todo 这里是全量的topic,添加topic则需要同时在下面的数组添加;也需要在produce_window.go中添加新的订阅者
+	AllTopics = []string{
+		TopicOfCicvTicker,                    // 0
+		TopicOfAmrPose,                       // 1
+		TopicOfBoundingBoxesFast,             // 2
+		TopicOfCameraFault,                   // 3
+		TopicOfCanData,                       // 4
+		TopicOfCh128x1LslidarPointCloud,      // 5
+		TopicOfCh64wLLslidarPointCloud,       // 6
+		TopicOfCh64wLScan,                    // 7
+		TopicOfCh64wRLslidarPointCloud,       // 8
+		TopicOfCh64wRScan,                    // 9
+		TopicOfCicvLidarclusterMovingObjects, // 10
+		TopicOfCicvAmrTrajectory,             // 11
+		TopicOfCicvLocation,                  // 12
+		TopicOfCloudClusters,                 // 13
+		TopicOfHeartbeatInfo,                 // 14
+		TopicOfLidarPretreatmentCost,         // 15
+		TopicOfLidarPretreatmentOdometry,     // 16
+		TopicOfLidarRoi,                      // 17
+		TopicOfLine1,                         // 18
+		TopicOfLine2,                         // 19
+		TopicOfMapPolygon,                    // 20
+		TopicOfObstacleDisplay,               // 21
+		TopicOfPjControlPub,                  // 22
+		TopicOfPointsCluster,                 // 23
+		TopicOfPointsConcat,                  // 24
+		TopicOfReferenceDisplay,              // 25
+		TopicOfReferenceTrajectory,           // 26
+		TopicOfRoiPoints,                     // 27
+		TopicOfRoiPolygon,                    // 28
+		TopicOfTf,                            // 29
+		TopicOfTpperception,                  // 30
+		TopicOfTpperceptionVis,               // 31
+		TopicOfTprouteplan,                   // 32
+		TopicOfTrajectoryDisplay,             // 33
+		TopicOfUngroundCloudpoints,           // 34
+		TopicOfCameraImage,                   // 35
+		TopicOfDataRead,                      // 36
+		TopicOfPjiGps,                        // 37
+		TopicOfFaultInfo,                     // 38
+		TopicOfPjVehicleFdbPub,               // 39
+		TopicOfEndPointMessage,               // 40
+	}
+
+	AllTopicsNumber = len(AllTopics)
+)

+ 58 - 0
aarch64/geely/master/main.go

@@ -0,0 +1,58 @@
+package main
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/geely/common/config"
+	commonService "cicv-data-closedloop/aarch64/pjisuv/common/service"
+	masterConfig "cicv-data-closedloop/aarch64/pjisuv/master/config"
+	masterService "cicv-data-closedloop/aarch64/pjisuv/master/service"
+	"cicv-data-closedloop/common/config/c_log"
+	_ "embed"
+)
+
+func init() {
+
+	// 初始化本地配置文件(第1处配置,在本地文件)
+	commonConfig.InitLocalConfig()
+
+	// runtime.GOMAXPROCS(1) // (可选)
+	c_log.InitLog(commonConfig.LogDir, commonConfig.MasterLogPrefix) // 初始化日志配置
+	//c_log.InitLog("/userdata/cicv-data-closedloop/log/", "pjisuv-master")
+
+	// 初始化Oss连接信息
+	commonConfig.InitOssConfig()
+	// 初始化业务逻辑配置信息,配置文件在oss上(第2处配置,在oss文件)
+	commonConfig.InitCloudConfig()
+	// 初始化数据闭环平台的配置(第3处配置,在数据闭环平台接口)
+	commonConfig.InitPlatformConfig()
+	// 初始化ros节点
+	commonConfig.InitRosConfig()
+	// 发送资源占用信息
+	go commonConfig.SendResourceUsage()
+	// 维护data目录缓存的包数量
+	go commonService.BagCacheClean()
+	// 磁盘占用过高时根据缓存策略处理copy目录
+	go commonService.DiskClean()
+	// 初始化加载触发器插件文件
+	masterConfig.InitTriggerConfig()
+	// 初始化rpc监听
+	commonConfig.InitKillSignalListener(commonConfig.CloudConfig.Hosts[0].Ip)
+	// 等待重启,接收到重启信号,会把信号分发给以下channel
+	go commonService.WaitKillSelf()
+	//// 定时上传故障码日志
+	//masterService.ForFaultCodeLog()
+}
+
+func main() {
+
+	// 1 负责打包数据到data目录
+	go commonService.BagRecord(commonConfig.CloudConfig.Hosts[0].Name)
+	// 2 启动第4个线程,负责监控故障,并修改timeWindow
+	go masterService.ProduceWindow()
+	// 3
+	go masterService.RunTimeWindowProducerQueue()
+	// 4 排队运行时间窗口
+	go commonService.RunTimeWindowConsumerQueue()
+
+	// 阻塞主线程,等待其他线程执行。
+	select {}
+}

+ 207 - 0
aarch64/geely/master/service/for_competition.go

@@ -0,0 +1,207 @@
+package service
+
+//
+//import (
+//	"cicv-data-closedloop/aarch64/pjisuv/common/config"
+//	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+//	"cicv-data-closedloop/common/config/c_log"
+//	"cicv-data-closedloop/common/util"
+//	"cicv-data-closedloop/pjisuv_msgs"
+//	"github.com/bluenviron/goroslib/v2"
+//	"github.com/bluenviron/goroslib/v2/pkg/msgs/std_msgs"
+//	"os"
+//	"strings"
+//	"sync"
+//	"time"
+//)
+//
+//var (
+//	dir       = "/userdata/competition/"
+//	dirBak    = "/userdata/competition-bak/"
+//	ossPrefix = "competition/"
+//	// v20240604 添加 /pj_vehicle_fdb_pub 判断人工接管场景
+//	commandArgs            = []string{"record", "--split", "--duration=1", "/pji_gps", "/data_read", "/pj_vehicle_fdb_pub"}
+//	topic                  = "/team_name"
+//	urlExamTick            = "http://36.110.106.142:12341/web_server/exam/tick"
+//	urlExamBegin           = "http://36.110.106.142:12341/web_server/exam/begin"
+//	urlExamEnd             = "http://36.110.106.142:12341/web_server/exam/end"
+//	cacheMutex             sync.Mutex
+//	cacheTeamName          = make(map[string]time.Time)
+//	heartBeatTimeThreshold = 5 * time.Second // 心跳时间
+//	bakSecondNumber        = 3600
+//	cachePositionX         = -999.00
+//	cachePositionY         = -999.00
+//	cacheAutoMode          = -1                // 自动驾驶状态 0 人工 1 自动
+//	InitialPositionX       = 565266.0482367771 // todo 需要比赛确认起点
+//	InitialPositionY       = 4329773.48728322  // todo 需要比赛确认起点
+//)
+//
+//// todo 实车比赛临时使用
+//// history record命令无法录制()
+//func ForCompetition() {
+//	go dataCollection()
+//	go dataCollectionBak(bakSecondNumber) // 需要再车上备份的数据,防止网络差传不回来
+//	go location()                         // 记录经纬度和速度
+//	go mode()                             // 记录自动驾驶状态
+//	go tick()
+//}
+//
+//// 全量数据采集
+//func dataCollection() {
+//	c_log.GlobalLogger.Info("开始采集实车算法比赛全量数据。")
+//	util.CreateDir(dir)
+//	// 1 打包
+//	c_log.GlobalLogger.Info("采集实车算法比赛全量数据的环境变量为:", commonConfig.RosbagEnvs)
+//	command, err := util.ExecuteWithEnvAndDirAsync(commonConfig.RosbagEnvs, dir, commonConfig.RosbagPath, commandArgs...)
+//	if err != nil {
+//		c_log.GlobalLogger.Error("程序崩溃。执行record命令", command, "出错:", err)
+//		os.Exit(-1)
+//	}
+//	// 2 扫描目录文件
+//	for {
+//		time.Sleep(time.Duration(2) * time.Second)
+//		files, _ := util.ListAbsolutePathAndSort(dir)
+//		if len(files) >= 2 {
+//			//c_log.GlobalLogger.Info("扫描实车比赛数据采集目录,", files)
+//			for i := range files {
+//				if i == len(files)-1 { // 最后一个包在录制中,不上传
+//					break
+//				}
+//				c_log.GlobalLogger.Debug("上传实车算法比赛全量数据包", files[i])
+//				bagSlice := strings.Split(files[i], "/")
+//				commonConfig.OssMutex.Lock()
+//				_ = commonConfig.OssBucket.PutObjectFromFile(ossPrefix+config.LocalConfig.EquipmentNo+"/"+bagSlice[len(bagSlice)-1], files[i])
+//				commonConfig.OssMutex.Unlock()
+//				_ = util.DeleteFile(files[i])
+//			}
+//		}
+//	}
+//}
+//
+//// 全量数据采集
+//func dataCollectionBak(bakSecondNumber int) {
+//	c_log.GlobalLogger.Info("开始备份实车算法比赛全量数据。")
+//	util.CreateDir(dirBak)
+//	// 1 打包
+//	command, err := util.ExecuteWithEnvAndDirAsync(commonConfig.RosbagEnvs, dirBak, commonConfig.RosbagPath, commandArgs...)
+//	if err != nil {
+//		c_log.GlobalLogger.Error("程序崩溃。备份目录执行record命令", command, "出错:", err)
+//		os.Exit(-1)
+//	}
+//	// 2 扫描目录文件
+//	for {
+//		time.Sleep(time.Duration(2) * time.Second)
+//		files, _ := util.ListAbsolutePathAndSort(dirBak)
+//		if len(files) >= bakSecondNumber {
+//			// 超出阈值就删除心跳+1个文件
+//			_ = util.DeleteFile(files[0])
+//			_ = util.DeleteFile(files[1])
+//			_ = util.DeleteFile(files[2])
+//		}
+//	}
+//}
+//
+//// data格式为队伍编号
+//// 保存单次考试时间区间
+//func location() {
+//	_, _ = goroslib.NewSubscriber(goroslib.SubscriberConf{
+//		Node:  commonConfig.RosNode,
+//		Topic: "/cicv_location",
+//		Callback: func(data *pjisuv_msgs.PerceptionLocalization) {
+//			cachePositionX = data.PositionX
+//			cachePositionY = data.PositionY
+//		},
+//	})
+//}
+//
+//func mode() {
+//	_, _ = goroslib.NewSubscriber(goroslib.SubscriberConf{
+//		Node:  commonConfig.RosNode,
+//		Topic: "/pj_vehicle_fdb_pub",
+//		Callback: func(data *pjisuv_msgs.VehicleFdb) {
+//			cacheAutoMode = int(data.Automode)
+//		},
+//	})
+//}
+//
+//// data格式为队伍编号
+//// 保存单次考试时间区间
+//func tick() {
+//	_, _ = goroslib.NewSubscriber(goroslib.SubscriberConf{
+//		Node:  commonConfig.RosNode,
+//		Topic: topic,
+//		Callback: func(data *std_msgs.String) {
+//			if cacheAutoMode == 1 { // 只有在自动驾驶状态才发送心跳
+//				//if math.Abs(positionX-InitialPositionX) > 5.00 || math.Abs(positionY-InitialPositionY) > 5.00 { // todo 测试临时使用起点判断
+//				teamName := data.Data
+//				response, err := util.HttpPostJsonWithHeaders(
+//					urlExamTick,
+//					map[string]string{"Authorization": "U9yKpD6kZZDDe4LFKK6myAxBUT1XRrDM"},
+//					map[string]string{
+//						"teamName":    teamName,
+//						"positionX":   util.ToString(cachePositionX),
+//						"positionY":   util.ToString(cachePositionY),
+//						"equipmentNo": config.LocalConfig.EquipmentNo, // 设备编号
+//					},
+//				)
+//				if err != nil {
+//					c_log.GlobalLogger.Infof("队伍 %v 的心跳发送到云端报错,错误信息为:%v。", teamName, err)
+//				}
+//				c_log.GlobalLogger.Infof("队伍 %v 的心跳发送到云端,响应结果为:%v。", teamName, response)
+//			}
+//		},
+//	})
+//}
+//
+////// data格式为队伍编号
+////// 保存单次考试时间区间
+////func examBeginBak() {
+////	_, _ = goroslib.NewSubscriber(goroslib.SubscriberConf{
+////		Node:  commonConfig.RosNode,
+////		Topic: topic,
+////		Callback: func(data *std_msgs.String) {
+////			teamName := data.Data
+////			cacheMutex.Lock()
+////			{
+////				if !util.ContainsKey(cacheTeamName, teamName) { // 1 如果缓存数组中没有此队名,代表考试开始,缓存此队名,和当前时间戳
+////					examBeginTime := time.Now()
+////					cacheTeamName[teamName] = examBeginTime
+////					_, _ = util.HttpPostJsonWithHeaders(
+////						urlExamBegin,
+////						map[string]string{"Authorization": "U9yKpD6kZZDDe4LFKK6myAxBUT1XRrDM"},
+////						map[string]string{"teamName": teamName},
+////					)
+////					c_log.GlobalLogger.Infof("队伍 %v 的考试开始。", teamName)
+////				} else { // 2 如果缓存数组中有此队名,代表考试进行中,刷新时间戳
+////					cacheTeamName[teamName] = time.Now()
+////				}
+////			}
+////			cacheMutex.Unlock()
+////		},
+////	})
+////}
+////
+////func examEndBak() {
+////	for {
+////		time.Sleep(time.Duration(1) * time.Second)
+////		cacheMutex.Lock()
+////		{
+////			var keysToDelete []string
+////			for teamName, heartBeatTime := range cacheTeamName {
+////				if time.Since(heartBeatTime) > heartBeatTimeThreshold { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
+////					keysToDelete = append(keysToDelete, teamName)
+////				}
+////			}
+////			for _, teamName := range keysToDelete { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
+////				delete(cacheTeamName, teamName)
+////				_, _ = util.HttpPostJsonWithHeaders(
+////					urlExamEnd,
+////					map[string]string{"Authorization": "U9yKpD6kZZDDe4LFKK6myAxBUT1XRrDM"},
+////					map[string]string{"teamName": teamName},
+////				)
+////				c_log.GlobalLogger.Infof("队伍 %v 的考试结束。", teamName)
+////			}
+////		}
+////		cacheMutex.Unlock()
+////	}
+////}

+ 88 - 0
aarch64/geely/master/service/for_fault_log.go

@@ -0,0 +1,88 @@
+package service
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/aarch64/pjisuv/master/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"cicv-data-closedloop/pjisuv_msgs"
+	"fmt"
+	"github.com/bluenviron/goroslib/v2"
+	"github.com/sirupsen/logrus"
+	"os"
+	"path/filepath"
+	"runtime"
+	"time"
+)
+
+var (
+	faultLogger *logrus.Logger
+	logPath     string
+	regularTime = 1 * time.Hour // 日志上传周期
+
+)
+
+func ForFaultCodeLog() {
+	initLog("/mnt/media/sda1/cicv-data-closedloop/fault_code/", "fault_code")
+	receiveFault()
+	//uploadLodRegularly()
+}
+
+// 初始化日志配置
+func initLog(logDir string, prefix string) {
+	time.Sleep(time.Duration(1) * time.Second)
+	// 创建、追加、读写,777,所有权限
+	logPath = logDir + prefix + "-" + time.Now().Format("2006-01-02-15-04-05") + ".log"
+	err := util.CreateParentDir(logPath)
+	if err != nil {
+		os.Exit(-1)
+	}
+	f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
+	if err != nil {
+		os.Exit(-1)
+	}
+	faultLogger = logrus.New()
+	faultLogger.SetOutput(f)
+	faultLogger.SetReportCaller(true) // 开启行号显示
+	faultLogger.SetFormatter(&logrus.JSONFormatter{
+		CallerPrettyfier: func(frame *runtime.Frame) (string, string) {
+			fileName := filepath.Base(frame.File)
+			return "", fmt.Sprintf("%s:%d", fileName, frame.Line)
+		},
+	})
+	faultLogger.Info("初始化 faultLogger - 成功")
+}
+
+func receiveFault() {
+	_, _ = goroslib.NewSubscriber(goroslib.SubscriberConf{
+		Node:  commonConfig.RosNode,
+		Topic: config.TopicOfFaultInfo,
+		Callback: func(data *pjisuv_msgs.FaultVec) {
+			codeString := ""
+			for _, info := range data.InfoVec {
+				codeString = codeString + util.ToString(info.ErrorCode)
+			}
+			faultLogger.Errorf("接收到故障码:【%v】", codeString)
+		},
+	})
+}
+
+func uploadLodRegularly() {
+	// 1 创建一个定时器,每隔一秒触发一次
+	ticker := time.NewTicker(regularTime)
+	// 2 使用goroutine运行一个无限循环
+	go func() {
+		for {
+			select {
+			// 定时器触发时执行的代码
+			case <-ticker.C:
+				commonConfig.OssMutex.Lock()
+				err := commonConfig.OssBucket.PutObjectFromFile(commonConfig.OssEquBasePrefix+"fault_code/"+filepath.Base(logPath), logPath)
+				commonConfig.OssMutex.Unlock()
+				if err != nil {
+					c_log.GlobalLogger.Error("上传故障码日志失败:", err)
+				}
+			}
+		}
+	}()
+}

+ 102 - 0
aarch64/geely/master/service/move_bag_and_send_window.go

@@ -0,0 +1,102 @@
+package service
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	commonService "cicv-data-closedloop/aarch64/pjisuv/common/service"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/domain"
+	"cicv-data-closedloop/common/entity"
+	"cicv-data-closedloop/common/util"
+	"net"
+	"time"
+)
+
+// 依次处理时间窗口,从data目录移动到copy目录
+func RunTimeWindowProducerQueue() {
+	c_log.GlobalLogger.Info("生产者队列goroutine - 启动")
+	for {
+		// 1 监控自杀信号
+		select {
+		case signal := <-commonService.ChannelKillMove:
+			if signal == 1 {
+				commonService.ChannelKillMove <- 1
+				if len(entity.TimeWindowProducerQueue) == 0 {
+					commonService.AddKillTimes("4")
+					return
+				}
+			} else { //signal == 2
+				commonService.AddKillTimes("4")
+				return
+			}
+		default:
+		}
+		time.Sleep(time.Duration(1) * time.Second)
+		// 2 处理
+		if len(entity.TimeWindowProducerQueue) == 0 {
+			continue
+		}
+		currentTimeWindow := entity.TimeWindowProducerQueue[0]                                         // 当前窗口
+		bags, _ := util.ListAbsolutePathWithSuffixAndSort(commonConfig.CloudConfig.BagDataDir, ".bag") // data目录的bag包列表
+		move := false
+		bigger := false
+		for _, bag := range bags {
+			bagTime := util.GetBagTime(bag) // bag包对应的时间
+			// 3 如果bag时间在窗口时间区间内,则从data移动到copy
+			if util.BagTimeInInterval(bagTime, currentTimeWindow.TimeWindowBegin, currentTimeWindow.TimeWindowEnd) {
+				domain.MoveFromDataToCopy(currentTimeWindow.FaultTime, commonConfig.CloudConfig.BagDataDir, bag, commonConfig.CloudConfig.BagCopyDir)
+				move = true
+			} else {
+				// 4 如果已经生成了新的包,则当前窗口已经移动完成了
+				if util.TimeCustom1GreaterEqualThanTimeCustom2(bagTime, currentTimeWindow.TimeWindowBegin) {
+					bigger = true
+					break
+				}
+			}
+		}
+		// 5 如果没有包可以供当前窗口移动,且已经生成了更新的包,则当前窗口已经可以上传
+		if !move && bigger {
+			currentTimeWindow.CanUpload = "yes"
+			// 如果第一个已经大于了timeWindowEnd,则触发上传并删除
+			// todo 校验补齐机制,可能损耗性能,暂时搁置
+			//domain.SupplyCopyBags(commonConfig.CloudConfig.BagDataDir, commonConfig.CloudConfig.BagCopyDir, currentTimeWindow)
+			entity.RefreshTcpSendTime()
+			go sendTimeWindowByTcp(currentTimeWindow)
+			entity.RemoveHeadOfTimeWindowProducerQueue()
+			entity.AddTimeWindowToTimeWindowConsumerQueue(currentTimeWindow)
+			// 获取copy目录下的字典json,key为触发时间,value为label
+			timeToLabelJson, _ := util.ReadFile(commonConfig.CloudConfig.TimeToLabelJsonPath)
+			timeToLabelMap, _ := util.JsonStringToMap(timeToLabelJson)
+			if timeToLabelMap == nil {
+				timeToLabelMap = make(map[string]interface{})
+			}
+			timeToLabelMap[currentTimeWindow.FaultTime] = util.ToString(currentTimeWindow.Labels)
+			timeToLabelJson, _ = util.MapToJsonString(timeToLabelMap)
+			_ = util.WriteFile(timeToLabelJson, commonConfig.CloudConfig.TimeToLabelJsonPath)
+			continue
+		} else { // 保证当前窗口只发送一次,每间隔5秒发一次非yes窗口
+			if int(time.Since(entity.TcpSendTime).Seconds()) > commonConfig.CloudConfig.TimeWindowSendGap {
+				currentTimeWindow.CanUpload = "no"
+				//c_log.GlobalLogger.Info("每隔", commonConfig.CloudConfig.TimeWindowSendGap, "秒发送一次tcp消息")
+				entity.RefreshTcpSendTime()
+				go sendTimeWindowByTcp(currentTimeWindow)
+			}
+		}
+	}
+}
+
+func sendTimeWindowByTcp(timeWindow entity.TimeWindow) {
+	socket := commonConfig.CloudConfig.Hosts[1].Ip + ":" + commonConfig.CloudConfig.TcpPort
+	tcpConn, err := net.Dial("tcp", socket)
+	if err != nil {
+		c_log.GlobalLogger.Error("建立tcp连接", socket, "失败:", err)
+		return
+	}
+	defer tcpConn.Close()
+	// 发送数据
+	send, _ := entity.TimeWindowToJson(timeWindow)
+	_, err = tcpConn.Write([]byte(send))
+	if err != nil {
+		c_log.GlobalLogger.Error("master发送给slave时间窗口", timeWindow, "失败:", err)
+		return
+	}
+}

+ 2304 - 0
aarch64/geely/master/service/produce_window.go

@@ -0,0 +1,2304 @@
+package service
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/aarch64/pjisuv/common/service"
+	masterConfig "cicv-data-closedloop/aarch64/pjisuv/master/config"
+	"cicv-data-closedloop/common/config/c_log"
+	commonEntity "cicv-data-closedloop/common/entity"
+	"cicv-data-closedloop/common/util"
+	"cicv-data-closedloop/pjisuv_msgs"
+	"cicv-data-closedloop/pjisuv_ticker"
+	"github.com/bluenviron/goroslib/v2"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/geometry_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/nav_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/sensor_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/tf2_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/visualization_msgs"
+	"math"
+	"sync"
+	"time"
+)
+
+type Weather struct {
+	WeatherID   []int
+	temperature float64
+	humidity    float64
+}
+type Point struct {
+	x, y float64
+}
+type Location struct {
+	Latitude  float64
+	Longitude float64
+}
+
+var (
+	LeftCurveFlag  bool = false
+	RightCurveFlag bool = false //用来判断车辆是左转还是右转
+	//定义了车间四个边界点,将车间抽象成一个四边形
+	LocationCount = 0
+	vertices      = []Point{
+		{x: 456128.413, y: 4397847.78},
+		{x: 456288.257, y: 4397953.51},
+		{x: 456359.022, y: 4397822.84},
+		{x: 456191.065, y: 4397733.3},
+	}
+	//定义园区部门T字路口的经纬度坐标值
+	point3 = Location{39.73040966605621, 116.48995329696209}
+	point4 = Location{39.73083727413453, 116.49079780188244}
+	point5 = Location{39.72976753711939, 116.49043130389033}
+	point6 = Location{39.73012466515933, 116.49128381717591}
+	point7 = Location{39.729251498328246, 116.49077484625299}
+	point8 = Location{39.72964529630643, 116.49164592200161}
+
+	pointlist = []Location{point3, point4, point5, point6, point7, point8}
+
+	cicvLocationTime = time.Now()
+	// -----------------------------共享变量
+	//cicv_location
+	//NumOfCicvLocation = 0
+	SpeedSlice   = []float64{}
+	AccelXSlice  = []float64{}
+	JunctionFlag = false
+	//Yowslice          = make([]float64, 0)
+	//AngleSlice        = make([][]float64, 0)
+	// /tpperception
+	countSideBySide           int     = 0
+	Frame                     float32 = 0.0
+	ObjDicOfTpperception              = make(map[uint32][][]float32)
+	objTypeDicOfTpperception          = make(map[uint32]uint8)
+	objSpeedDicOfTpperception         = make(map[uint32]float64)
+	PreCloseTargetSlice               = []uint32{}
+	// /pji_control_pub
+	numCountPjiControlCommandOfPjControlPub int
+	egoSteeringCmdOfPjControlPub            []float64
+	egoThrottleCmdOfPjControlPub            []float64
+	// /data_read
+	numCountDataReadOfDataRead int
+	egoSteeringRealOfDataRead  []float64
+	egoThrottleRealOfDataRead  []float64
+	GearPosSlice               = []int16{}
+	// --------------------------------------------------
+	shareVars           = new(sync.Map)
+	saveTimeWindowMutex sync.Mutex // 保存时间窗口需要锁,防止数据竟态
+	latestTimeWindowEnd = util.GetTimeCustom(time.Now())
+	triggerInterval     = 3.0 // 每个触发器3秒触发一次
+)
+
+// 负责监听所有主题并修改时间窗口
+func ProduceWindow() {
+	c_log.GlobalLogger.Info("订阅者 goroutine,启动。")
+
+	var err error
+	subscribers := make([]*goroslib.Subscriber, masterConfig.AllTopicsNumber)
+	subscribersTimes := make([]time.Time, masterConfig.AllTopicsNumber)
+	subscribersTimeMutexes := make([]sync.Mutex, masterConfig.AllTopicsNumber)
+	subscribersMutexes := make([]sync.Mutex, masterConfig.AllTopicsNumber)
+	for i, topic := range masterConfig.AllTopics {
+		for {
+			// 定时器,区别于订阅者
+			if topic == pjisuv_ticker.TickerTopic {
+				// 1 把所有触发器函数执行一遍,触发器内部创建额外的定时任务goroutine
+				for _, f := range masterConfig.RuleOfCicvTicker {
+					f(shareVars)
+				}
+				// 2 创建goroutine接收定时任务触发器返回的Label和Time,执行触发逻辑
+				go func() {
+					for {
+						time.Sleep(time.Duration(1)) // 降低循环频率
+						select {
+						case tickInfo := <-pjisuv_ticker.TickerChan:
+							faultLabel := tickInfo.FaultLabel
+							faultHappenTime := tickInfo.FaultHappenTime
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+						default:
+						}
+					}
+				}()
+			}
+			create := false // 判断是否创建成功,用于打印日志
+			// 1
+			if topic == masterConfig.TopicOfAmrPose && (len(masterConfig.RuleOfAmrPose1) > 0 || len(masterConfig.RuleOfAmrPose3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *visualization_msgs.MarkerArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfAmrPose1) > 0 {
+								for _, f := range masterConfig.RuleOfAmrPose1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfAmrPose3) > 0 {
+								for _, f := range masterConfig.RuleOfAmrPose3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 2
+			if topic == masterConfig.TopicOfBoundingBoxesFast &&
+				(len(masterConfig.RuleOfBoundingBoxesFast1) > 0 ||
+					len(masterConfig.RuleOfBoundingBoxesFast3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.BoundingBoxArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfBoundingBoxesFast1) > 0 {
+								for _, f := range masterConfig.RuleOfBoundingBoxesFast1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfBoundingBoxesFast3) > 0 {
+								for _, f := range masterConfig.RuleOfBoundingBoxesFast3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 3
+			if topic == masterConfig.TopicOfCameraFault &&
+				(len(masterConfig.RuleOfCameraFault1) > 0 ||
+					len(masterConfig.RuleOfCameraFault3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.FaultVec) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCameraFault1) > 0 {
+								for _, f := range masterConfig.RuleOfCameraFault1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCameraFault3) > 0 {
+								for _, f := range masterConfig.RuleOfCameraFault3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 4
+			if topic == masterConfig.TopicOfCanData &&
+				(len(masterConfig.RuleOfCanData1) > 0 ||
+					len(masterConfig.RuleOfCanData3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.Frame) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCanData1) > 0 {
+								for _, f := range masterConfig.RuleOfCanData1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCanData3) > 0 {
+								for _, f := range masterConfig.RuleOfCanData3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 5
+			if topic == masterConfig.TopicOfCh128x1LslidarPointCloud &&
+				(len(masterConfig.RuleOfCh128x1LslidarPointCloud1) > 0 ||
+					len(masterConfig.RuleOfCh128x1LslidarPointCloud3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCh128x1LslidarPointCloud1) > 0 {
+								for _, f := range masterConfig.RuleOfCh128x1LslidarPointCloud1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCh128x1LslidarPointCloud3) > 0 {
+								for _, f := range masterConfig.RuleOfCh128x1LslidarPointCloud3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 6
+			if topic == masterConfig.TopicOfCh64wLLslidarPointCloud &&
+				(len(masterConfig.RuleOfCh64wLLslidarPointCloud1) > 0 ||
+					len(masterConfig.RuleOfCh64wLLslidarPointCloud3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCh64wLLslidarPointCloud1) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wLLslidarPointCloud1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCh64wLLslidarPointCloud3) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wLLslidarPointCloud3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 7
+			if topic == masterConfig.TopicOfCh64wLScan &&
+				(len(masterConfig.RuleOfCh64wLScan1) > 0 ||
+					len(masterConfig.RuleOfCh64wLScan3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.LaserScan) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCh64wLScan1) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wLScan1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCh64wLScan3) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wLScan3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 8
+			if topic == masterConfig.TopicOfCh64wRLslidarPointCloud &&
+				(len(masterConfig.RuleOfCh64wRLslidarPointCloud1) > 0 ||
+					len(masterConfig.RuleOfCh64wRLslidarPointCloud3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCh64wRLslidarPointCloud1) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wRLslidarPointCloud1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCh64wRLslidarPointCloud3) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wRLslidarPointCloud3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 9
+			if topic == masterConfig.TopicOfCh64wRScan &&
+				(len(masterConfig.RuleOfCh64wRScan1) > 0 ||
+					len(masterConfig.RuleOfCh64wRScan3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.LaserScan) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCh64wRScan1) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wRScan1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCh64wRScan3) > 0 {
+								for _, f := range masterConfig.RuleOfCh64wRScan3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 10
+			if topic == masterConfig.TopicOfCicvLidarclusterMovingObjects &&
+				(len(masterConfig.RuleOfCicvLidarclusterMovingObjects1) > 0 ||
+					len(masterConfig.RuleOfCicvLidarclusterMovingObjects3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.PerceptionCicvMovingObjects) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCicvLidarclusterMovingObjects1) > 0 {
+								for _, f := range masterConfig.RuleOfCicvLidarclusterMovingObjects1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCicvLidarclusterMovingObjects3) > 0 {
+								for _, f := range masterConfig.RuleOfCicvLidarclusterMovingObjects3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 11 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfCicvAmrTrajectory {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.Trajectory) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCicvAmrTrajectory1) > 0 {
+								for _, f := range masterConfig.RuleOfCicvAmrTrajectory1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCicvAmrTrajectory3) > 0 {
+								for _, f := range masterConfig.RuleOfCicvAmrTrajectory3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+						// 更新共享变量
+
+						Trajectorypoints := data.Trajectoryinfo.Trajectorypoints
+						if len(Trajectorypoints) > 2 {
+							StartHeading := Trajectorypoints[0].Heading
+							EndHeading := Trajectorypoints[len(Trajectorypoints)-1].Heading
+							diffHeading := StartHeading - EndHeading
+							//fmt.Println(diffHeading)
+
+							if diffHeading < -1.0 && diffHeading > -3.0 {
+								LeftCurveFlag = true
+								//fmt.Println(diffHeading)
+							} else {
+								LeftCurveFlag = false
+							}
+							if diffHeading > 1.0 && diffHeading < 3.0 {
+								RightCurveFlag = true
+								//fmt.Println(diffHeading)
+							} else {
+								RightCurveFlag = false
+							}
+
+						} else {
+							LeftCurveFlag = false
+							RightCurveFlag = false
+						}
+						shareVars.Store("LeftCurveFlag", LeftCurveFlag)
+						shareVars.Store("RightCurveFlag", RightCurveFlag)
+
+						currentCurvateres := make([]float64, 0)
+						for _, point := range data.Trajectoryinfo.Trajectorypoints {
+							currentCurvateres = append(currentCurvateres, math.Abs(float64(point.Curvature)))
+						}
+						shareVars.Store("LastCurvaturesOfCicvAmrTrajectory", currentCurvateres)
+						shareVars.Store("DecisionType", data.Trajectoryinfo.DecisionType)
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 12 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfCicvLocation {
+				LocationCount++
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.PerceptionLocalization) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCicvLocation1) > 0 {
+								for _, f := range masterConfig.RuleOfCicvLocation1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCicvLocation3) > 0 {
+								for _, f := range masterConfig.RuleOfCicvLocation3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+						// 更新共享变量
+						if LocationCount%10 == 0 {
+							enterflag := IfEnter(pointlist, 12.0, data.Latitude, data.Longitude)
+							shareVars.Store("EnterJunctionFlag", enterflag)
+							LocationCount = 0
+						}
+
+						AbsSpeed := math.Sqrt(math.Pow(data.VelocityX, 2) + math.Pow(data.VelocityY, 2))
+						AccelXSlice = append(AccelXSlice, data.AccelX)
+						shareVars.Store("AbsSpeed", AbsSpeed)
+						shareVars.Store("AccelXSlice", AccelXSlice)
+						shareVars.Store("VelocityXOfCicvLocation", data.VelocityX)
+						shareVars.Store("VelocityYOfCicvLocation", data.VelocityY)
+						shareVars.Store("VelocityZOfCicvLocation", data.VelocityZ)
+						shareVars.Store("YawOfCicvLocation", data.Yaw)
+						shareVars.Store("AngularVelocityZOfCicvLocation", data.AngularVelocityZ)
+						shareVars.Store("PositionXOfCicvLocation", data.PositionX)
+						shareVars.Store("PositionYOfCicvLocation", data.PositionY)
+						shareVars.Store("Latitude", data.Latitude)
+						shareVars.Store("Longitude", data.Longitude)
+						//SpeedSlice 用于自车频繁起停触发器-FrequentStartsAndStops
+						SpeedSlice = append(SpeedSlice, AbsSpeed)
+						if len(SpeedSlice) > 6000 {
+							SpeedSlice = SpeedSlice[1:]
+						}
+						shareVars.Store("SpeedSlice", SpeedSlice)
+						/*用于 陡坡-DescendingSteepHill/ClimbingSteepHill 侧翻预警-RolloverWarning 触发器
+						if NumOfCicvLocation%10==0{
+							AngleSlice[0] = append(AngleSlice[0], data.Qx)
+							AngleSlice[1] = append(AngleSlice[1], data.Qy)
+							AngleSlice[2] = append(AngleSlice[2], data.Qz)
+							AngleSlice[3] = append(AngleSlice[3], data.Qw)
+							shareVars.Store("AngleSlice", AngleSlice)
+							NumOfCicvLocation=1
+						}
+						NumOfCicvLocation++
+						*/
+
+						/*用于 掉头TurnAround触发器
+						Yowslice = append(Yowslice, data.Yaw)
+						if len(Yowslice) >= 600 {
+							Yowslice = Yowslice[1:]
+						}
+						shareVars.Store("Yowslice", Yowslice)
+						*/
+
+						// 用于判断是否在车间内
+						if time.Since(cicvLocationTime).Seconds() > 1 {
+							p := Point{x: data.PositionX, y: data.PositionY}
+							OutsideWorkshopFlag := isPointInPolygon(p, vertices) //在车间返回0,不在车间返回1
+							shareVars.Store("OutsideWorkshopFlag", OutsideWorkshopFlag)
+							cicvLocationTime = time.Now()
+						}
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 13
+			if topic == masterConfig.TopicOfCloudClusters &&
+				(len(masterConfig.RuleOfCloudClusters1) > 0 ||
+					len(masterConfig.RuleOfCloudClusters3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.AutowareCloudClusterArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCloudClusters1) > 0 {
+								for _, f := range masterConfig.RuleOfCloudClusters1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCloudClusters3) > 0 {
+								for _, f := range masterConfig.RuleOfCloudClusters3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 14
+			if topic == masterConfig.TopicOfHeartbeatInfo &&
+				(len(masterConfig.RuleOfHeartbeatInfo1) > 0 ||
+					len(masterConfig.RuleOfHeartbeatInfo3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.HeartBeatInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfHeartbeatInfo1) > 0 {
+								for _, f := range masterConfig.RuleOfHeartbeatInfo1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfHeartbeatInfo3) > 0 {
+								for _, f := range masterConfig.RuleOfHeartbeatInfo3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 15
+			if topic == masterConfig.TopicOfLidarPretreatmentCost &&
+				(len(masterConfig.RuleOfLidarPretreatmentCost1) > 0 ||
+					len(masterConfig.RuleOfLidarPretreatmentCost3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *geometry_msgs.Vector3Stamped) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfLidarPretreatmentCost1) > 0 {
+								for _, f := range masterConfig.RuleOfLidarPretreatmentCost1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfLidarPretreatmentCost3) > 0 {
+								for _, f := range masterConfig.RuleOfLidarPretreatmentCost3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 16
+			if topic == masterConfig.TopicOfLidarPretreatmentOdometry &&
+				(len(masterConfig.RuleOfLidarPretreatmentOdometry1) > 0 ||
+					len(masterConfig.RuleOfLidarPretreatmentOdometry3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *nav_msgs.Odometry) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfLidarPretreatmentOdometry1) > 0 {
+								for _, f := range masterConfig.RuleOfLidarPretreatmentOdometry1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfLidarPretreatmentOdometry3) > 0 {
+								for _, f := range masterConfig.RuleOfLidarPretreatmentOdometry3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 17
+			if topic == masterConfig.TopicOfLidarRoi &&
+				(len(masterConfig.RuleOfLidarRoi1) > 0 ||
+					len(masterConfig.RuleOfLidarRoi3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *geometry_msgs.PolygonStamped) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfLidarRoi1) > 0 {
+								for _, f := range masterConfig.RuleOfLidarRoi1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfLidarRoi3) > 0 {
+								for _, f := range masterConfig.RuleOfLidarRoi3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 18
+			if topic == masterConfig.TopicOfLine1 &&
+				(len(masterConfig.RuleOfLine11) > 0 ||
+					len(masterConfig.RuleOfLine13) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *nav_msgs.Path) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfLine11) > 0 {
+								for _, f := range masterConfig.RuleOfLine11 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfLine13) > 0 {
+								for _, f := range masterConfig.RuleOfLine13 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 19
+			if topic == masterConfig.TopicOfLine2 &&
+				(len(masterConfig.RuleOfLine21) > 0 ||
+					len(masterConfig.RuleOfLine23) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *nav_msgs.Path) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfLine21) > 0 {
+								for _, f := range masterConfig.RuleOfLine21 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfLine23) > 0 {
+								for _, f := range masterConfig.RuleOfLine23 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 20
+			if topic == masterConfig.TopicOfMapPolygon &&
+				(len(masterConfig.RuleOfMapPolygon1) > 0 ||
+					len(masterConfig.RuleOfMapPolygon3) > 0) {
+
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.PolygonStamped) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfMapPolygon1) > 0 {
+								for _, f := range masterConfig.RuleOfMapPolygon1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfMapPolygon3) > 0 {
+								for _, f := range masterConfig.RuleOfMapPolygon3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 21
+			if topic == masterConfig.TopicOfObstacleDisplay &&
+				(len(masterConfig.RuleOfObstacleDisplay1) > 0 ||
+					len(masterConfig.RuleOfObstacleDisplay3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *visualization_msgs.MarkerArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfObstacleDisplay1) > 0 {
+								for _, f := range masterConfig.RuleOfObstacleDisplay1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfObstacleDisplay3) > 0 {
+								for _, f := range masterConfig.RuleOfObstacleDisplay3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 22 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfPjControlPub {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.CommonVehicleCmd) {
+
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfPjControlPub1) > 0 {
+								for _, f := range masterConfig.RuleOfPjControlPub1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfPjControlPub3) > 0 {
+								for _, f := range masterConfig.RuleOfPjControlPub3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+						// 更新共享变量
+						numCountPjiControlCommandOfPjControlPub++
+						if numCountPjiControlCommandOfPjControlPub == 10 {
+							egoSteeringCmdOfPjControlPub = append(egoSteeringCmdOfPjControlPub, data.ICPVCmdStrAngle)
+							egoThrottleCmdOfPjControlPub = append(egoThrottleCmdOfPjControlPub, data.ICPVCmdAccPelPosAct)
+							numCountPjiControlCommandOfPjControlPub = 0
+						}
+						shareVars.Store("NumCountPjiControlCommandOfPjControlPub", numCountPjiControlCommandOfPjControlPub)
+						shareVars.Store("EgoSteeringCmdOfPjControlPub", egoSteeringCmdOfPjControlPub)
+						shareVars.Store("EgoThrottleCmdOfPjControlPub", egoThrottleCmdOfPjControlPub)
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 23
+			if topic == masterConfig.TopicOfPointsCluster &&
+				(len(masterConfig.RuleOfPointsCluster1) > 0 ||
+					len(masterConfig.RuleOfPointsCluster3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfPointsCluster1) > 0 {
+								for _, f := range masterConfig.RuleOfPointsCluster1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfPointsCluster3) > 0 {
+								for _, f := range masterConfig.RuleOfPointsCluster3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 24
+			if topic == masterConfig.TopicOfPointsConcat &&
+				(len(masterConfig.RuleOfPointsConcat1) > 0 ||
+					len(masterConfig.RuleOfPointsConcat3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfPointsConcat1) > 0 {
+								for _, f := range masterConfig.RuleOfPointsConcat1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfPointsConcat3) > 0 {
+								for _, f := range masterConfig.RuleOfPointsConcat3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 25
+			if topic == masterConfig.TopicOfReferenceDisplay &&
+				(len(masterConfig.RuleOfReferenceDisplay1) > 0 ||
+					len(masterConfig.RuleOfReferenceDisplay3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *nav_msgs.Path) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfReferenceDisplay1) > 0 {
+								for _, f := range masterConfig.RuleOfReferenceDisplay1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfReferenceDisplay3) > 0 {
+								for _, f := range masterConfig.RuleOfReferenceDisplay3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 26
+			if topic == masterConfig.TopicOfReferenceTrajectory &&
+				(len(masterConfig.RuleOfReferenceTrajectory1) > 0 ||
+					len(masterConfig.RuleOfReferenceTrajectory3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.Trajectory) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfReferenceTrajectory1) > 0 {
+								for _, f := range masterConfig.RuleOfReferenceTrajectory1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfReferenceTrajectory3) > 0 {
+								for _, f := range masterConfig.RuleOfReferenceTrajectory3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 27
+			if topic == masterConfig.TopicOfRoiPoints &&
+				(len(masterConfig.RuleOfRoiPoints1) > 0 ||
+					len(masterConfig.RuleOfRoiPoints3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfRoiPoints1) > 0 {
+								for _, f := range masterConfig.RuleOfRoiPoints1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfRoiPoints3) > 0 {
+								for _, f := range masterConfig.RuleOfRoiPoints3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 28
+			if topic == masterConfig.TopicOfRoiPolygon &&
+				(len(masterConfig.RuleOfRoiPolygon1) > 0 ||
+					len(masterConfig.RuleOfRoiPolygon3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *nav_msgs.Path) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfRoiPolygon1) > 0 {
+								for _, f := range masterConfig.RuleOfRoiPolygon1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfRoiPolygon3) > 0 {
+								for _, f := range masterConfig.RuleOfRoiPolygon3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 29
+			if topic == masterConfig.TopicOfTf &&
+				(len(masterConfig.RuleOfTf1) > 0 ||
+					len(masterConfig.RuleOfTf3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *tf2_msgs.TFMessage) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfTf1) > 0 {
+								for _, f := range masterConfig.RuleOfTf1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfTf3) > 0 {
+								for _, f := range masterConfig.RuleOfTf3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 30 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfTpperception {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.PerceptionObjects) {
+
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfTpperception1) > 0 {
+								for _, f := range masterConfig.RuleOfTpperception1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfTpperception3) > 0 {
+								for _, f := range masterConfig.RuleOfTpperception3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+
+						// 更新共享变量
+						for _, obj := range data.Objs {
+							YawOfCicvLocation, _ := shareVars.Load("YawOfCicvLocation")
+							diffh := float64(obj.Heading) - YawOfCicvLocation.(float64)
+							if diffh < -180.0 {
+								diffh = 360.0 + diffh
+							} else if diffh > 180.0 {
+								diffh = 360.0 - diffh
+							} else {
+								diffh = math.Abs(diffh)
+							}
+
+							if math.Abs(float64(obj.X)) >= 30 || math.Abs(float64(obj.Y)) >= 30 {
+								continue
+							}
+							if _, ok := ObjDicOfTpperception[obj.Id]; !ok {
+								ObjDicOfTpperception[obj.Id] = [][]float32{{}, {}, {}, {}, {}, {}, {}, {}}
+							}
+							ObjDicOfTpperception[obj.Id][0] = append(ObjDicOfTpperception[obj.Id][0], obj.X)
+							ObjDicOfTpperception[obj.Id][1] = append(ObjDicOfTpperception[obj.Id][1], obj.Y)
+							ObjDicOfTpperception[obj.Id][2] = append(ObjDicOfTpperception[obj.Id][2], obj.Vxrel)
+							absspeed := math.Sqrt(math.Pow(float64(obj.Vxabs), 2) + math.Pow(float64(obj.Vyabs), 2))
+							ObjDicOfTpperception[obj.Id][3] = append(ObjDicOfTpperception[obj.Id][3], float32(absspeed))
+							ObjDicOfTpperception[obj.Id][4] = append(ObjDicOfTpperception[obj.Id][4], obj.Heading)
+							ObjDicOfTpperception[obj.Id][5] = append(ObjDicOfTpperception[obj.Id][5], Frame)
+							ObjDicOfTpperception[obj.Id][6] = append(ObjDicOfTpperception[obj.Id][6], float32(obj.Type))
+							ObjDicOfTpperception[obj.Id][7] = append(ObjDicOfTpperception[obj.Id][7], float32(diffh))
+							if len(ObjDicOfTpperception[obj.Id][0]) >= 100 {
+								ObjDicOfTpperception[obj.Id][0] = ObjDicOfTpperception[obj.Id][0][1:]
+								ObjDicOfTpperception[obj.Id][1] = ObjDicOfTpperception[obj.Id][1][1:]
+								ObjDicOfTpperception[obj.Id][2] = ObjDicOfTpperception[obj.Id][2][1:]
+								ObjDicOfTpperception[obj.Id][3] = ObjDicOfTpperception[obj.Id][3][1:]
+								ObjDicOfTpperception[obj.Id][4] = ObjDicOfTpperception[obj.Id][4][1:]
+								ObjDicOfTpperception[obj.Id][5] = ObjDicOfTpperception[obj.Id][5][1:]
+								ObjDicOfTpperception[obj.Id][6] = ObjDicOfTpperception[obj.Id][6][1:]
+								ObjDicOfTpperception[obj.Id][7] = ObjDicOfTpperception[obj.Id][7][1:]
+							}
+
+							objTypeDicOfTpperception[obj.Id] = obj.Type
+							objSpeedDicOfTpperception[obj.Id] = math.Pow(math.Pow(float64(obj.Vxabs), 2)+math.Pow(float64(obj.Vyabs), 2), 0.5)
+						}
+						shareVars.Store("ObjDicOfTpperception", ObjDicOfTpperception)
+						shareVars.Store("ObjTypeDicOfTpperception", objTypeDicOfTpperception)
+						shareVars.Store("ObjSpeedDicOfTpperception", objSpeedDicOfTpperception)
+						shareVars.Store("countSideBySide", countSideBySide)
+
+						Frame++
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 31
+			if topic == masterConfig.TopicOfTpperceptionVis &&
+				(len(masterConfig.RuleOfTpperceptionVis1) > 0 ||
+					len(masterConfig.RuleOfTpperceptionVis3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *visualization_msgs.MarkerArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfTpperceptionVis1) > 0 {
+								for _, f := range masterConfig.RuleOfTpperceptionVis1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfTpperceptionVis3) > 0 {
+								for _, f := range masterConfig.RuleOfTpperceptionVis3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 32
+			if topic == masterConfig.TopicOfTprouteplan &&
+				(len(masterConfig.RuleOfTprouteplan1) > 0 ||
+					len(masterConfig.RuleOfTprouteplan3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.RoutePlan) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfTprouteplan1) > 0 {
+								for _, f := range masterConfig.RuleOfTprouteplan1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfTprouteplan3) > 0 {
+								for _, f := range masterConfig.RuleOfTprouteplan3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 33
+			if topic == masterConfig.TopicOfTrajectoryDisplay &&
+				(len(masterConfig.RuleOfTrajectoryDisplay1) > 0 ||
+					len(masterConfig.RuleOfTrajectoryDisplay3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *nav_msgs.Path) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfTrajectoryDisplay1) > 0 {
+								for _, f := range masterConfig.RuleOfTrajectoryDisplay1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfTrajectoryDisplay3) > 0 {
+								for _, f := range masterConfig.RuleOfTrajectoryDisplay3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 34
+			if topic == masterConfig.TopicOfUngroundCloudpoints &&
+				(len(masterConfig.RuleOfUngroundCloudpoints1) > 0 ||
+					len(masterConfig.RuleOfUngroundCloudpoints3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.PointCloud2) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfUngroundCloudpoints1) > 0 {
+								for _, f := range masterConfig.RuleOfUngroundCloudpoints1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfUngroundCloudpoints3) > 0 {
+								for _, f := range masterConfig.RuleOfUngroundCloudpoints3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 35
+			if topic == masterConfig.TopicOfCameraImage &&
+				(len(masterConfig.RuleOfCameraImage1) > 0 ||
+					len(masterConfig.RuleOfCameraImage3) > 0) {
+
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.Image) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfCameraImage1) > 0 {
+								for _, f := range masterConfig.RuleOfCameraImage1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfCameraImage3) > 0 {
+								for _, f := range masterConfig.RuleOfCameraImage3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+
+			// 36 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfDataRead {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.Retrieval) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfDataRead1) > 0 {
+								for _, f := range masterConfig.RuleOfDataRead1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfDataRead3) > 0 {
+								for _, f := range masterConfig.RuleOfDataRead3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+						// 更新共享变量
+						numCountDataReadOfDataRead++
+						if numCountDataReadOfDataRead == 10 {
+							egoSteeringRealOfDataRead = append(egoSteeringRealOfDataRead, data.ActStrWhAng)
+							egoThrottleRealOfDataRead = append(egoThrottleRealOfDataRead, data.AccPed2)
+							numCountDataReadOfDataRead = 0
+						}
+						GearPosSlice = append(GearPosSlice, data.GearPos)
+						shareVars.Store("NumCountDataReadOfDataRead", numCountDataReadOfDataRead)
+						shareVars.Store("EgoSteeringRealOfDataRead", egoSteeringRealOfDataRead)
+						shareVars.Store("EgoThrottleRealOfDataRead", egoThrottleRealOfDataRead)
+						shareVars.Store("ActStrWhAngOfDataRead", data.ActStrWhAng)
+						shareVars.Store("GearPosSlice", GearPosSlice)
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 37
+			if topic == masterConfig.TopicOfPjiGps &&
+				(len(masterConfig.RuleOfPjiGps1) > 0 ||
+					len(masterConfig.RuleOfPjiGps3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.PerceptionLocalization) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfPjiGps1) > 0 {
+								for _, f := range masterConfig.RuleOfPjiGps1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfPjiGps3) > 0 {
+								for _, f := range masterConfig.RuleOfPjiGps3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 38
+			if topic == masterConfig.TopicOfFaultInfo &&
+				(len(masterConfig.RuleOfFaultInfo1) > 0 ||
+					len(masterConfig.RuleOfFaultInfo3) > 0) {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.FaultVec) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfFaultInfo1) > 0 {
+								for _, f := range masterConfig.RuleOfFaultInfo1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										for _, infoVec := range data.InfoVec {
+											c_log.GlobalLogger.Info("接收到故障码:", infoVec.ErrorCode)
+										}
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfFaultInfo3) > 0 {
+								for _, f := range masterConfig.RuleOfFaultInfo3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										for _, infoVec := range data.InfoVec {
+											c_log.GlobalLogger.Info("接收到故障码:", infoVec.ErrorCode)
+										}
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 39 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfPjVehicleFdbPub {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjisuv_msgs.VehicleFdb) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfPjVehicleFdbPub1) > 0 {
+								for _, f := range masterConfig.RuleOfPjVehicleFdbPub1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfPjVehicleFdbPub3) > 0 {
+								for _, f := range masterConfig.RuleOfPjVehicleFdbPub3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+						// 更新共享变量
+						shareVars.Store("AutomodeOfPjVehicleFdbPub", data.Automode)
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 40 有共享变量的订阅者必须被创建
+			if topic == masterConfig.TopicOfEndPointMessage {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *geometry_msgs.Point) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := util.GetNowTimeCustom()         // 获取当前故障发生时间
+							lastTimeWindow := commonEntity.GetLastTimeWindow() // 获取最后一个时间窗口
+							faultLabel := ""
+							if len(masterConfig.RuleOfEndPointMessage1) > 0 {
+								for _, f := range masterConfig.RuleOfEndPointMessage1 {
+									faultLabel = f(data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+							if len(masterConfig.RuleOfEndPointMessage3) > 0 {
+								for _, f := range masterConfig.RuleOfEndPointMessage3 {
+									faultLabel = f(shareVars, data)
+									if faultLabel != "" {
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										subscribersTimes[i] = time.Now()
+										goto TriggerSuccess
+									}
+								}
+							}
+						TriggerSuccess:
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+
+						// 更新共享变量
+						shareVars.Store("EndPointX", data.X)
+						shareVars.Store("EndPointY", data.Y)
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			if err != nil {
+				c_log.GlobalLogger.Infof("创建订阅者报错,可能由于节点未启动,再次尝试【%v】", err)
+				time.Sleep(time.Duration(2) * time.Second)
+				continue
+			} else {
+				if create {
+					c_log.GlobalLogger.Infof("创建订阅者订阅话题【%v】", topic)
+				}
+				break
+			}
+		}
+
+	}
+	c_log.GlobalLogger.Infof("全部订阅者创建完成。")
+	select {
+	case signal := <-service.ChannelKillWindowProducer:
+		if signal == 1 {
+			commonConfig.RosNode.Close()
+			service.AddKillTimes("3")
+			return
+		}
+	}
+}
+
+func saveTimeWindow(faultLabel string, faultHappenTime string, lastTimeWindow *commonEntity.TimeWindow) {
+	var taskBeforeTime, taskAfterTime, taskMaxTime int
+	{
+		if commonConfig.PlatformConfig.TaskBeforeTime == 0 {
+			taskBeforeTime = 5
+		} else {
+			taskBeforeTime = commonConfig.PlatformConfig.TaskBeforeTime
+		}
+		if commonConfig.PlatformConfig.TaskAfterTime == 0 {
+			taskAfterTime = 4
+		} else {
+			taskAfterTime = commonConfig.PlatformConfig.TaskAfterTime
+		}
+		if commonConfig.PlatformConfig.TaskMaxTime == 0 {
+			taskMaxTime = 60
+		} else {
+			taskMaxTime = commonConfig.PlatformConfig.TaskMaxTime
+		}
+	}
+	saveTimeWindowMutex.Lock()
+	defer saveTimeWindowMutex.Unlock()
+	masterTopics, slaveTopics := getTopicsOfNode(faultLabel)
+	if lastTimeWindow == nil || util.TimeCustom1GreaterTimeCustom2(faultHappenTime, lastTimeWindow.TimeWindowEnd) { // 如果是不在旧故障窗口内,添加一个新窗口
+		exceptBegin := util.TimeCustomChange(faultHappenTime, -taskBeforeTime)
+		finalTimeWindowBegin := ""
+		if util.TimeCustom1LessEqualThanTimeCustom2(exceptBegin, latestTimeWindowEnd) { // 窗口最早时间不能早于上一个窗口结束时间
+			finalTimeWindowBegin = latestTimeWindowEnd
+		} else {
+			finalTimeWindowBegin = exceptBegin
+		}
+		latestTimeWindowEnd = util.TimeCustomChange(faultHappenTime, taskAfterTime)
+		newTimeWindow := commonEntity.TimeWindow{
+			FaultTime:       faultHappenTime,
+			TimeWindowBegin: finalTimeWindowBegin,
+			TimeWindowEnd:   latestTimeWindowEnd,
+			Length:          util.CalculateDifferenceOfTimeCustom(finalTimeWindowBegin, latestTimeWindowEnd),
+			Labels:          []string{faultLabel},
+			MasterTopics:    masterTopics,
+			SlaveTopics:     slaveTopics,
+		}
+		c_log.GlobalLogger.Infof("不在旧故障窗口内,向生产者队列添加一个新窗口,【Lable】=%v,【FaultTime】=%v,【Length】=%v", newTimeWindow.Labels, newTimeWindow.FaultTime, newTimeWindow.Length)
+
+		commonEntity.AddTimeWindowToTimeWindowProducerQueue(newTimeWindow)
+	} else { // 如果在旧故障窗口内
+		commonEntity.TimeWindowProducerQueueMutex.RLock()
+		defer commonEntity.TimeWindowProducerQueueMutex.RUnlock()
+		// 更新故障窗口end时间
+		expectEnd := util.TimeCustomChange(faultHappenTime, taskAfterTime) // 窗口期望关闭时间是触发时间加上后置时间
+		expectLength := util.CalculateDifferenceOfTimeCustom(lastTimeWindow.TimeWindowBegin, expectEnd)
+		if expectLength < taskMaxTime {
+			latestTimeWindowEnd = expectEnd
+			lastTimeWindow.TimeWindowEnd = latestTimeWindowEnd
+			lastTimeWindow.Length = util.CalculateDifferenceOfTimeCustom(lastTimeWindow.TimeWindowBegin, lastTimeWindow.TimeWindowEnd)
+		}
+		// 更新label
+		labels := lastTimeWindow.Labels
+		lastTimeWindow.Labels = util.AppendIfNotExists(labels, faultLabel)
+		// 更新 topic
+		sourceMasterTopics := lastTimeWindow.MasterTopics
+		lastTimeWindow.MasterTopics = util.MergeSlice(sourceMasterTopics, masterTopics)
+		sourceSlaveTopics := lastTimeWindow.SlaveTopics
+		lastTimeWindow.SlaveTopics = util.MergeSlice(sourceSlaveTopics, slaveTopics)
+		c_log.GlobalLogger.Infof("在旧故障窗口内,更新生产者队列最新的窗口,【Lable】=%v,【FaultTime】=%v,【Length】=%v", lastTimeWindow.Labels, lastTimeWindow.FaultTime, lastTimeWindow.Length)
+	}
+}
+
+func getTopicsOfNode(faultLabel string) (masterTopics []string, slaveTopics []string) {
+	// 获取所有需要采集的topic
+	var faultCodeTopics []string
+	for _, code := range commonConfig.CloudConfig.Triggers {
+		if code.Label == faultLabel {
+			faultCodeTopics = code.Topics
+		}
+	}
+	// 根据不同节点采集的topic进行分配采集
+	for _, acceptTopic := range faultCodeTopics {
+		for _, host := range commonConfig.CloudConfig.Hosts {
+			for _, topic := range host.Topics {
+				if host.Name == commonConfig.CloudConfig.Hosts[0].Name && acceptTopic == topic {
+					masterTopics = append(masterTopics, acceptTopic)
+				}
+				if host.Name == commonConfig.CloudConfig.Hosts[1].Name && acceptTopic == topic {
+					slaveTopics = append(slaveTopics, acceptTopic)
+				}
+			}
+		}
+	}
+	return masterTopics, slaveTopics
+}
+func isPointInPolygon(p Point, vertices []Point) bool {
+	intersections := 0
+	for i := 0; i < len(vertices); i++ {
+		j := (i + 1) % len(vertices)
+		if rayIntersectsSegment(p, vertices[i], vertices[j]) {
+			intersections++
+		}
+	}
+	return !(intersections%2 == 1)
+}
+
+func rayIntersectsSegment(p, p1, p2 Point) bool {
+	if p1.y > p2.y {
+		p1, p2 = p2, p1
+	}
+	if p.y == p1.y || p.y == p2.y {
+		p.y += 0.0001
+	}
+
+	if p.y < p1.y || p.y > p2.y {
+		return false
+	}
+
+	if p.x > max(p1.x, p2.x) {
+		return false
+	}
+
+	if p.x < min(p1.x, p2.x) {
+		return true
+	}
+
+	blueSlope := (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y) + p1.x
+	return p.x < blueSlope
+}
+
+func min(a, b float64) float64 {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+func max(a, b float64) float64 {
+	if a > b {
+		return a
+	}
+	return b
+}
+
+// 判断Ego是否在交叉口,在的话返回true
+func IfEnter(pointlist []Location, radius float64, lat, lon float64) bool {
+	// 判断是否进入点列表中的区域
+	point1 := Location{Latitude: lat, Longitude: lon}
+	for _, point := range pointlist {
+		d := distance(point1, point)
+		if d <= radius {
+			return true
+		}
+	}
+	return false
+}
+
+// 计算两点之间的距离(米)
+func distance(point1, point2 Location) float64 {
+	// 经纬度转弧度
+	lat1 := point1.Latitude * math.Pi / 180
+	lon1 := point1.Longitude * math.Pi / 180
+	lat2 := point2.Latitude * math.Pi / 180
+	lon2 := point2.Longitude * math.Pi / 180
+
+	// 计算距离
+	dlon := lon2 - lon1
+	dlat := lat2 - lat1
+	a := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Sin(dlon/2)*math.Sin(dlon/2)*math.Cos(lat1)*math.Cos(lat2)
+	c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
+	d := 6371000 * c
+	return d
+}

+ 55 - 0
aarch64/geely/slave/main.go

@@ -0,0 +1,55 @@
+package main
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	commonService "cicv-data-closedloop/aarch64/pjisuv/common/service"
+	slaveConfig "cicv-data-closedloop/aarch64/pjisuv/slave/package/config"
+	slaveService "cicv-data-closedloop/aarch64/pjisuv/slave/package/service"
+	"cicv-data-closedloop/common/config/c_log"
+)
+
+// init 初始化函数
+func init() {
+	//runtime.GOMAXPROCS(1)
+	c_log.InitLog(commonConfig.LogDir, commonConfig.SlaveLogPrefix)
+	// 初始化本地配置文件(第1处配置,在本地文件)
+	commonConfig.InitLocalConfig()
+	// 初始化Oss连接信息
+	commonConfig.InitOssConfig()
+	// 初始化业务逻辑配置信息,配置文件在oss上(第2处配置,在oss文件)
+	commonConfig.InitCloudConfig()
+	if commonConfig.CloudConfig.RefreshCloudConfig {
+		go commonConfig.RefreshCloudConfig()
+	}
+	// 初始化数据闭环平台的配置(第3处配置,在数据闭环平台接口)
+	commonConfig.InitPlatformConfig()
+	// 初始化ros节点
+	commonConfig.InitRosConfig()
+	// 发送资源占用信息
+	go commonConfig.SendResourceUsage()
+	// 维护data目录缓存的包数量
+	go commonService.BagCacheClean()
+	// 磁盘占用过高时根据缓存策略处理copy目录
+	go commonService.DiskClean()
+	slaveConfig.InitTcpListener()
+	commonConfig.InitKillSignalListener(commonConfig.CloudConfig.Hosts[1].Ip)
+	// 等待重启,接收到重启信号,会把信号分发给以下channel
+	go commonService.WaitKillSelf()
+}
+
+// main 主函数
+func main() {
+
+	// 1 负责打包数据到data目录
+	go commonService.BagRecord(commonConfig.CloudConfig.Hosts[1].Name)
+	// 2 负责监控故障,并修改timeWindow
+	go slaveService.PrepareTimeWindowProducerQueue()
+	// 3
+	go slaveService.RunTimeWindowProducerQueue()
+	// 4 排队运行时间窗口
+	go commonService.RunTimeWindowConsumerQueue()
+
+	// 阻塞主线程,等待其他线程执行。
+	select {}
+
+}

+ 22 - 0
aarch64/geely/slave/package/config/slave_tcp_config.go

@@ -0,0 +1,22 @@
+package config
+
+import (
+	"cicv-data-closedloop/aarch64/pjisuv/common/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"net"
+	"os"
+)
+
+var TcpListener net.Listener
+
+func InitTcpListener() {
+	c_log.GlobalLogger.Info("从节点初始化TCP端口监听 - 开始。")
+	socket := config.CloudConfig.Hosts[1].Ip + ":" + config.CloudConfig.TcpPort
+	var err error
+	TcpListener, err = net.Listen("tcp", socket)
+	if err != nil {
+		c_log.GlobalLogger.Error("监听tcp端口失败:", err)
+		os.Exit(-1)
+	}
+	c_log.GlobalLogger.Info("从节点初始化TCP端口监听 - 成功:", socket)
+}

+ 62 - 0
aarch64/geely/slave/package/service/accept_window.go

@@ -0,0 +1,62 @@
+package service
+
+import (
+	"cicv-data-closedloop/aarch64/pjisuv/common/service"
+	"cicv-data-closedloop/aarch64/pjisuv/slave/package/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/entity"
+	"context"
+	"encoding/json"
+	"sync"
+)
+
+func PrepareTimeWindowProducerQueue() {
+	var prepareTimeWindowProducerQueueMutex sync.Mutex
+	ctx, cancel := context.WithCancel(context.Background())
+	// 处理退出信号
+	go func() {
+		select {
+		case signal := <-service.ChannelKillWindowProducer:
+			if signal == 1 {
+				cancel()
+				config.TcpListener.Close()
+				service.AddKillTimes("3")
+				return
+			}
+		}
+	}()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		default:
+			conn, err := config.TcpListener.Accept()
+			if err != nil {
+				select {
+				case <-ctx.Done():
+					return
+				default:
+					c_log.GlobalLogger.Error("接受连接错误:", err)
+					continue
+				}
+			}
+			prepareTimeWindowProducerQueueMutex.Lock()
+			buffer := make([]byte, 2048)
+			total, err := conn.Read(buffer)
+			if err != nil {
+				c_log.GlobalLogger.Error("读取数据错误:", err)
+				continue
+			}
+			var timeWindow entity.TimeWindow
+			err = json.Unmarshal(buffer[:total], &timeWindow)
+			if err != nil {
+				c_log.GlobalLogger.Error("解析Json时出错:", err)
+				continue
+			}
+			entity.AddTimeWindowToTimeWindowProducerQueue(timeWindow)
+			prepareTimeWindowProducerQueueMutex.Unlock()
+		}
+
+	}
+}

+ 71 - 0
aarch64/geely/slave/package/service/move_bag.go

@@ -0,0 +1,71 @@
+package service
+
+import (
+	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
+	commonService "cicv-data-closedloop/aarch64/pjisuv/common/service"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/domain"
+	"cicv-data-closedloop/common/entity"
+	"cicv-data-closedloop/common/util"
+	"time"
+)
+
+// 将时间窗口内的包全部move出去,并等待当前时间窗口结束触发上传
+func RunTimeWindowProducerQueue() {
+	c_log.GlobalLogger.Info("生产者队列 - 启动")
+	for { // 必须串行排队处理
+		select {
+		case signal := <-commonService.ChannelKillMove:
+			c_log.GlobalLogger.Info("生产者队列接收到自杀信号", signal)
+			if signal == 1 {
+				commonService.ChannelKillMove <- 1
+				if len(entity.TimeWindowProducerQueue) == 0 {
+					commonService.AddKillTimes("4")
+					return
+				}
+			} else { //signal == 2
+				commonService.AddKillTimes("4")
+				return
+			}
+		default:
+		}
+		time.Sleep(time.Duration(1) * time.Second)
+		if len(entity.TimeWindowProducerQueue) > 0 {
+			currentTimeWindow := entity.TimeWindowProducerQueue[0]
+			// 将时间窗口移出准备队列
+			entity.RemoveHeadOfTimeWindowProducerQueue()
+			if currentTimeWindow.CanUpload == "yes" {
+				c_log.GlobalLogger.Info("从节点接收到可上传的timeWindow")
+			}
+			if currentTimeWindow.CanUpload == "no" {
+				c_log.GlobalLogger.Info("从节点接收到不可上传的timeWindow")
+			}
+
+			// 2 timeWindow不可以上传,则将data目录下的数据move到copy目录
+			bags, _ := util.ListAbsolutePathWithSuffixAndSort(commonConfig.CloudConfig.BagDataDir, ".bag")
+			for _, bag := range bags {
+				bagTime := util.GetBagTime(bag)
+				compare1 := util.TimeCustom1GreaterEqualThanTimeCustom2(bagTime, currentTimeWindow.TimeWindowBegin)
+				compare2 := util.TimeCustom1LessEqualThanTimeCustom2(bagTime, currentTimeWindow.TimeWindowEnd)
+				if compare1 && compare2 {
+					// 将bag包移动到Copy目录
+					domain.MoveFromDataToCopy(currentTimeWindow.FaultTime, commonConfig.CloudConfig.BagDataDir, bag, commonConfig.CloudConfig.BagCopyDir)
+				} else {
+					if util.TimeCustom1GreaterEqualThanTimeCustom2(bagTime, currentTimeWindow.TimeWindowBegin) {
+						// 必须已经生成了窗口之后的包才算窗口结束了
+						break
+					}
+				}
+			}
+			// 判断是否可上传
+			if currentTimeWindow.CanUpload == "yes" {
+				// 1 timeWindow可以上传
+				c_log.GlobalLogger.Info("timeWindow可以上传:", currentTimeWindow)
+				// 补充bag包
+				//domain.SupplyCopyBags(commonConfig.CloudConfig.BagDataDir, commonConfig.CloudConfig.BagCopyDir, currentTimeWindow)
+				// 将时间窗口加入运行队列
+				entity.AddTimeWindowToTimeWindowConsumerQueue(currentTimeWindow)
+			}
+		}
+	}
+}