소스 검색

Merge remote-tracking branch 'origin/master'

Yishi Wang 9 달 전
부모
커밋
ec84e22f52
99개의 변경된 파일2716개의 추가작업 그리고 812개의 파일을 삭제
  1. 14 5
      README.md
  2. 0 33
      aarch64/pjibot_delivery/README.md
  3. 5 5
      aarch64/pjibot_delivery/common/config/c_platform.go
  4. 4 2
      aarch64/pjibot_delivery/common/service/disk_clean.go
  5. 34 32
      aarch64/pjibot_delivery/common/service/rosbag_record.go
  6. 8 3
      aarch64/pjibot_delivery/common/service/rosbag_upload.go
  7. 9 1
      aarch64/pjibot_delivery/control/main.go
  8. 97 71
      aarch64/pjibot_delivery/master/package/config/trigger_init.go
  9. 17 1
      aarch64/pjibot_delivery/master/package/config/trigger_var.go
  10. 3 0
      aarch64/pjibot_delivery/master/package/service/move_bag_and_send_window.go
  11. 329 36
      aarch64/pjibot_delivery/master/package/service/produce_window.go
  12. 0 0
      aarch64/pjibot_delivery/start-control.sh
  13. 0 0
      aarch64/pjibot_delivery/start-master.sh
  14. 1 1
      aarch64/pjibot_delivery/配送机器人默认配置文件-cloud-config.yaml
  15. 0 0
      aarch64/pjibot_delivery/配送机器人默认配置文件-local-config.yaml
  16. 0 33
      aarch64/pjibot_guide/README.md
  17. 5 5
      aarch64/pjibot_guide/common/config/c_platform.go
  18. 2 1
      aarch64/pjibot_guide/common/service/disk_clean.go
  19. 37 33
      aarch64/pjibot_guide/common/service/rosbag_record.go
  20. 7 1
      aarch64/pjibot_guide/common/service/rosbag_upload.go
  21. 7 1
      aarch64/pjibot_guide/control/main.go
  22. 0 140
      aarch64/pjibot_guide/master/package/config/master_trigger_cfg.go
  23. 142 0
      aarch64/pjibot_guide/master/package/config/trigger_init.go
  24. 43 0
      aarch64/pjibot_guide/master/package/config/trigger_var.go
  25. 4 0
      aarch64/pjibot_guide/master/package/service/move_bag_and_send_window.go
  26. 49 35
      aarch64/pjibot_guide/master/package/service/produce_window.go
  27. 0 0
      aarch64/pjibot_guide/start-control.sh
  28. 0 0
      aarch64/pjibot_guide/start-master.sh
  29. 1 1
      aarch64/pjibot_guide/引导机器人默认配置文件-cloud-config.yaml
  30. 0 0
      aarch64/pjibot_guide/引导机器人默认配置文件-local-config.yaml
  31. 0 33
      aarch64/pjibot_patrol/README.md
  32. 6 6
      aarch64/pjibot_patrol/common/config/c_platform.go
  33. 4 2
      aarch64/pjibot_patrol/common/service/disk_clean.go
  34. 34 32
      aarch64/pjibot_patrol/common/service/rosbag_record.go
  35. 7 1
      aarch64/pjibot_patrol/common/service/rosbag_upload.go
  36. 9 1
      aarch64/pjibot_patrol/control/main.go
  37. 97 70
      aarch64/pjibot_patrol/master/package/config/trigger_init.go
  38. 16 2
      aarch64/pjibot_patrol/master/package/config/trigger_var.go
  39. 3 0
      aarch64/pjibot_patrol/master/package/service/move_bag_and_send_window.go
  40. 329 37
      aarch64/pjibot_patrol/master/package/service/produce_window.go
  41. 0 0
      aarch64/pjibot_patrol/start-control.sh
  42. 0 0
      aarch64/pjibot_patrol/start-master.sh
  43. 1 1
      aarch64/pjibot_patrol/巡检机器人默认配置文件-cloud-config.yaml
  44. 0 0
      aarch64/pjibot_patrol/巡检机器人默认配置文件-local-config.yaml
  45. 0 15
      aarch64/pjisuv/README.md
  46. 5 5
      aarch64/pjisuv/common/config/c_cloud.go
  47. 5 5
      aarch64/pjisuv/common/config/c_platform.go
  48. 10 5
      aarch64/pjisuv/common/service/rosbag_record.go
  49. 32 5
      aarch64/pjisuv/common/service/rosbag_upload.go
  50. 1 1
      aarch64/pjisuv/master/config/trigger_init.go
  51. 90 40
      aarch64/pjisuv/master/config/trigger_var.go
  52. 139 15
      aarch64/pjisuv/master/service/produce_window.go
  53. 0 0
      aarch64/pjisuv/remove.sh
  54. 0 0
      aarch64/pjisuv/start-soc1.sh
  55. 0 0
      aarch64/pjisuv/start-soc2.sh
  56. 3 5
      aarch64/pjisuv/多功能车-cloud-config.yaml
  57. 0 0
      aarch64/pjisuv/多功能车-soc1-local-config.yaml
  58. 0 0
      aarch64/pjisuv/多功能车-soc2-local-config.yaml
  59. 29 0
      common/service/cache_monitor_queue.go
  60. 0 25
      kinglong_param/kinglong_param.go
  61. 5 0
      pjibot_delivery_msgs/common_msgs.go
  62. 5 0
      pjibot_patrol_msgs/common_msgs.go
  63. 2 2
      trigger/pjibot_delivery/imu/UnstableDriving/main/UnstableDriving.go
  64. 2 2
      trigger/pjibot_delivery/locate_info/LocationFailed/main/LocationFailed.go
  65. 11 7
      trigger/pjibot_delivery/robot_pose/EnterTjunction/main/EnterTjunction.go
  66. 12 11
      trigger/pjibot_delivery/robot_pose/PassManholeCover/main/PassManholeCover.go
  67. 2 2
      trigger/pjibot_delivery/sys_info/CpuOverOccupied/main/CpuOverOccupied.go
  68. 2 2
      trigger/pjibot_delivery/sys_info/MemOverOccupied/main/MemOverOccupied.go
  69. 1 1
      trigger/pjibot_delivery/task_feedback_info/TaskFailed/main/TaskFailed.go
  70. 49 0
      trigger/pjibot_delivery/wheel_odom/AuLongStop/main/AuLongStop.go
  71. 3 1
      trigger/pjisuv/cicv_location/AbnormalParking/main/AbnormalParking.go
  72. 10 3
      trigger/pjisuv/cicv_location/AbnormalStopOnCurve/main/AbnormalStopOnCurve.go
  73. 10 3
      trigger/pjisuv/cicv_location/AbnormalStopOnJunction/main/AbnormalStopOnJunction.go
  74. 35 0
      trigger/pjisuv/cicv_location/BrakeWithHighSpeed/main/BrakeWithHighSpeed.go
  75. 128 0
      trigger/pjisuv/cicv_location/CannotBypassObstacles/main/CannotBypassObstacles.go
  76. 3 3
      trigger/pjisuv/cicv_location/CurveOverspeed/main/CurveOverspeed.go
  77. 64 0
      trigger/pjisuv/cicv_location/EgoReversing/main/EgoReversing.go
  78. 2 2
      trigger/pjisuv/cicv_location/FindTrafficLightP1/main/FindTrafficLightP1.go
  79. 2 2
      trigger/pjisuv/cicv_location/FindTrafficLightP2/main/FindTrafficLightP2.go
  80. 2 2
      trigger/pjisuv/cicv_location/FindTrafficLightP3/main/FindTrafficLightP3.go
  81. 2 2
      trigger/pjisuv/cicv_location/FindTrafficLightP4/main/FindTrafficLightP4.go
  82. 3 3
      trigger/pjisuv/cicv_location/JunctionOverspeed/main/JunctionOverspeed.go
  83. 2 2
      trigger/pjisuv/cicv_location/OverSpeed/main/OverSpeed.go
  84. 90 0
      trigger/pjisuv/cicv_location/SpeedBumpOverspeed/main/SpeedBumpOverspeed.go
  85. 83 0
      trigger/pjisuv/cicv_location/TargetCarBehindWhenReversing/main/TargetCarBehindWhenReversing.go
  86. 15 8
      trigger/pjisuv/cicv_ticker/BeOvertakenInCorner/main/BeOvertakenInCorner.go
  87. 92 0
      trigger/pjisuv/cicv_ticker/BeOvertakenWithHighSpeed/main/BeOvertakenWithHighSpeed.go
  88. 96 0
      trigger/pjisuv/cicv_ticker/FrontCarDrivingWrongDirection/main/FrontCarDrivingWrongDirection.go
  89. 1 0
      trigger/pjisuv/cicv_ticker/FrontVehicleBrake/main/FrontVehicleBrake.go
  90. 127 0
      trigger/pjisuv/cicv_ticker/FrontVehicleBrakeInCurve/main/FrontVehicleBrakeInCurve.go
  91. 134 0
      trigger/pjisuv/cicv_ticker/FrontVehicleBrakeInJunction/main/FrontVehicleBrakeInJunction.go
  92. 2 1
      trigger/pjisuv/cicv_ticker/FrontVehicleCutInFar/main/FrontVehicleCutInFar.go
  93. 2 1
      trigger/pjisuv/cicv_ticker/FrontVehicleCutInNear/main/FrontVehicleCutInNear.go
  94. 1 1
      trigger/pjisuv/cicv_ticker/FrontVehicleCutOutFar/main/FrontVehicleCutOutFar.go
  95. 1 0
      trigger/pjisuv/cicv_ticker/FrontVehicleCutOutNear/main/FrontVehicleCutOutNear.go
  96. 71 0
      trigger/pjisuv/cicv_ticker/GearJump/main/GearJump.go
  97. 1 2
      trigger/pjisuv/cicv_ticker/HuaLong/main/HuaLong.go
  98. 3 2
      trigger/pjisuv/cicv_ticker/OutOperationZone/main/OutOperationZone.go
  99. 2 1
      trigger/pjisuv/cicv_ticker/RearVehicleApproach/main/RearVehicleApproach.go

+ 14 - 5
README.md

@@ -5,11 +5,8 @@
   - (新方案)采用 sync.Map,将触发器与 param 包解耦 
   - pjisuvParam 结构体不要再修改,已废弃方案
 - 添加共享变量时
-  - 如果有新的topic需要被订阅,需要在数据闭环平台上的topic序列添加
-    - 平台当前维护的序列(队列为触发器涉及的topic和共享变量涉及的topic的并集)
-      - 多功能车:/cicv_amr_trajectory,/cicv_extend,/cicv_location,/fault_info,/map_polygon,/pj_control_pub,/pj_vehicle_fdb_pub,/tpperception,/data_read
+  - 如果有新的topic需要被订阅,需要在 trigger_var.go 文件中添加
   - 需要修改produce_window.go文件中的订阅者,将触发器>0条件去掉
-- 
 
 # 一、代码目录结构
 
@@ -20,6 +17,15 @@
 ## 1-2 xxx_msgs目录
 - ros消息定义
 
+## aarch64/pjisuv
+## 目录结构
+### common
+- c_cloud.go:阿里云oss配置文件下载解析
+### control 调度程序
+### master 102 程序
+- trigger_init.go:102 初始化加载触发器插件
+### slave 103 程序
+
 # 二、oss 配置
 
 ```
@@ -30,4 +36,7 @@ oss-browser预设oss路径:oss://open-bucket
 oss-browser区域:华北2(北京)
 keyid:n8glvFGS25MrLY7j
 secret:xZ2Fozoarpfw0z28FUhtg8cu0yDc5d
-```
+```
+
+
+# 室外机器人

+ 0 - 33
aarch64/pjibot_delivery/README.md

@@ -1,33 +0,0 @@
-# 一、由于机器人由启用单个相机变成两个相机,所以话题有多变化
-1、只启用了单个相机的机器
-/amcl_pose
-/camera/color/image_raw
-/camera/depth/points
-/diagnostics
-/locate_info
-/obstacle_detection
-/odom
-/move_base/global_costmap/costmap
-/move_base/global_costmap/costmap_updates     
-/move_base/local_costmap/costmap
-/move_base/local_costmap/costmap_updates
-/scan
-/scan_map_icp_amcl_node/scan_point_transformed
-/sys_info
-# 2、启用了两个相机的机器
-/amcl_pose
-/ob_camera_01/color/image_raw(上边的摄像头)
-/ob_camera_01/depth/points(上边的摄像头)
-/ob_camera_02/color/image_raw(下边的摄像头)
-/ob_camera_02/depth/points(下边的摄像头)
-/diagnostics
-/locate_info
-/obstacle_detection(有引导任务的时候才会有)
-/odom
-/move_base/global_costmap/costmap
-/move_base/global_costmap/costmap_updates     
-/move_base/local_costmap/costmap
-/move_base/local_costmap/costmap_updates
-/scan
-/scan_map_icp_amcl_node/scan_point_transformed
-/sys_info

+ 5 - 5
aarch64/pjibot_delivery/common/config/c_platform.go

@@ -37,8 +37,8 @@ type response struct {
 }
 
 var (
-	PlatformConfig  PlatformConfigStruct
-	SubscribeTopics []string
+	PlatformConfig PlatformConfigStruct
+	RecordTopics   []string
 )
 
 // InitPlatformConfig 初始化数据闭环平台的配置
@@ -55,10 +55,10 @@ func InitPlatformConfig() {
 			continue
 		}
 		if checkPlatformConfig() {
-			SubscribeTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
+			RecordTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
 			// 去掉首尾空格
-			for i, topic := range SubscribeTopics {
-				SubscribeTopics[i] = strings.TrimSpace(topic)
+			for i, topic := range RecordTopics {
+				RecordTopics[i] = strings.TrimSpace(topic)
 			}
 			break
 		}

+ 4 - 2
aarch64/pjibot_delivery/common/service/disk_clean.go

@@ -79,8 +79,10 @@ func getIndexToRemoveForLRU() int {
 	for i >= 0 {
 		for i2, window := range entity.TimeWindowConsumerQueue {
 			for _, label := range window.Labels {
-				if masterConfig.LabelMapTriggerId[label] == lru[i] {
-					return i2
+				if value, ok := masterConfig.LabelMapTriggerId.Load(label); ok {
+					if value.(string) == lru[i] {
+						return i2
+					}
 				}
 			}
 		}

+ 34 - 32
aarch64/pjibot_delivery/common/service/rosbag_record.go

@@ -15,6 +15,7 @@ func BagRecord(nodeName string) {
 	var err error
 	c_log.GlobalLogger.Info("rosbag record goroutine - 启动")
 	for {
+	startRecord:
 		c_log.GlobalLogger.Info("校验必需的 rosnode 是否全部启动。")
 		canRecord := false
 		for !canRecord {
@@ -29,8 +30,13 @@ func BagRecord(nodeName string) {
 		command = append(command, "--duration=1")
 		for _, host := range config.CloudConfig.Hosts {
 			if host.Name == nodeName {
-				for _, topic := range host.Topics {
-					command = append(command, topic)
+				// 配置文件中的是node1和node2各自的所有topic,platformConfig中配置用户想缓存的topic,取交集
+				for _, topic1 := range config.RecordTopics {
+					for _, topic2 := range host.Topics {
+						if topic1 == topic2 {
+							command = append(command, topic1)
+						}
+					}
 				}
 			}
 		}
@@ -42,8 +48,8 @@ func BagRecord(nodeName string) {
 		var recordProcessPid int
 		var recordSubProcessPid int
 		var cmd *exec.Cmd
-		systemEnv := os.Environ()
-		c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
+		//systemEnv := os.Environ()
+		//c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
 	parent:
 		for {
 			c_log.GlobalLogger.Info("record 环境变量为:", config.RosbagEnvs)
@@ -73,40 +79,36 @@ func BagRecord(nodeName string) {
 		}
 		// 等待自杀信号
 		c_log.GlobalLogger.Info("启动record命令成功。等待自杀信号。")
-		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命令一直录包占满存储
+		for {
+			select {
+			case signal := <-ChannelKillRosRecord:
+				if signal == 1 {
+					kill(recordProcessPid, recordSubProcessPid, cmd)
+					AddKillTimes("1")
+					continue // continue 是为了等待重启信号
+				}
+				if signal == 2 {
+					goto startRecord // 收到信号 2 重新 record 命令
 				}
-				if err = cmd.Process.Kill(); err != nil {
-					c_log.GlobalLogger.Error("程序阻塞,杀死record命令进程", recordProcessPid, "出错:", err)
-					select {} // 此处阻塞防止record命令一直录包占满存储
+				if signal == 3 { // 这个关闭是等待数据处理时的关闭
+					c_log.GlobalLogger.Error("采集数据,接收record命令进程关闭信号:", signal)
+					kill(recordProcessPid, recordSubProcessPid, cmd)
+					continue
 				}
-				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 kill(recordProcessPid int, recordSubProcessPid int, cmd *exec.Cmd) {
+	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命令一直录包占满存储
+	}
+}
 func isCanRecord(n *goroslib.Node) bool {
 	time.Sleep(time.Duration(1) * time.Second)
 	// 获取

+ 8 - 3
aarch64/pjibot_delivery/common/service/rosbag_upload.go

@@ -77,7 +77,7 @@ outLoop:
 					continue
 				}
 				// 删除旧文件
-				util.DeleteFile(oldName)
+				_ = util.DeleteFile(oldName)
 				// 将新文件改回旧文件名
 				if err = os.Rename(newName, oldName); err != nil {
 					c_log.GlobalLogger.Info("修改文件名", oldName, "失败,放弃当前时间窗口", currentTimeWindow.FaultTime, ",错误为:", err)
@@ -123,7 +123,9 @@ outLoop:
 		// 在上传完成的包目录同级下添加一个目录同名的json
 		triggerIds := make([]string, 0)
 		for _, label := range currentTimeWindow.Labels {
-			triggerIds = append(triggerIds, masterConfig.LabelMapTriggerId[label])
+			if value, ok := masterConfig.LabelMapTriggerId.Load(label); ok {
+				triggerIds = append(triggerIds, value.(string))
+			}
 		}
 		callBackMap := map[string]interface{}{
 			"dataName":    currentTimeWindow.FaultTime, // 云端callback程序会将该值加8小时,因为UTC和CSV时区相差8小时
@@ -152,6 +154,9 @@ outLoop:
 		if err = util.RemoveDir(dir); err != nil {
 			continue outLoop
 		}
-
+		if len(entity.TimeWindowConsumerQueue) == 0 {
+			c_log.GlobalLogger.Infof("已处理所有窗口,重启 record 命令。")
+			ChannelKillRosRecord <- 2
+		}
 	}
 }

+ 9 - 1
aarch64/pjibot_delivery/control/main.go

@@ -27,10 +27,18 @@ func init() {
 }
 
 func main() {
+	init := true
+	turnLength := 60
 	lastStatus := "NONE"
 	//  轮询任务接口判断是否有更新
 	for {
-		time.Sleep(time.Duration(60) * time.Second)
+		c_log.GlobalLogger.Errorf("一轮次扫描时间【%v】秒:", turnLength)
+		if init {
+			time.Sleep(time.Duration(1) * time.Second)
+			init = false
+		} else {
+			time.Sleep(time.Duration(turnLength) * time.Second)
+		}
 		// 1 获取当前设备的任务的 status
 		status, err := commonConfig.GetStatus(commonConfig.PlatformConfig.TaskConfigId)
 		if err != nil {

+ 97 - 71
aarch64/pjibot_delivery/master/package/config/trigger_init.go

@@ -11,28 +11,57 @@ import (
 	"github.com/bluenviron/goroslib/v2/pkg/msgs/sensor_msgs"
 	"github.com/bluenviron/goroslib/v2/pkg/msgs/std_msgs"
 	"plugin"
-	"strconv"
+	"slices"
 )
 
 func InitTriggerConfig() {
-	loadSuccess := 0
-	// 下载所有触发器的文件
-	for _, trigger := range config.PlatformConfig.TaskTriggers {
-		// 下载
-		triggerLocalPath := config.CloudConfig.TriggersDir + trigger.TriggerScriptPath
+	config.OssMutex.Lock()
+	defer config.OssMutex.Unlock()
+	triggerLocalPathsMapTriggerId := make(map[string]string)
+	c_log.GlobalLogger.Info("主节点加载触发器插件 - 开始。")
+	// 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)
-		c_log.GlobalLogger.Info("下载触发器插件从", trigger.TriggerScriptPath, "到", triggerLocalPath)
-		config.OssMutex.Lock()
-		err := config.OssBucket.GetObjectToFile(trigger.TriggerScriptPath, triggerLocalPath)
-		config.OssMutex.Unlock()
-		if err != nil {
-			c_log.GlobalLogger.Errorf("下载oss上的触发器插件失败【%v】->【%v】:%v", trigger.TriggerScriptPath, triggerLocalPath, err)
-			continue
+		for {
+			if err := config.OssBucket.GetObjectToFile(trigger.TriggerScriptPath, triggerLocalPath); err != nil {
+				c_log.GlobalLogger.Error("下载触发器插件失败,再次尝试:", err)
+				continue
+			}
+			break
 		}
+		triggerLocalPathsMapTriggerId[triggerLocalPath] = id
+	}
+
+	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 {
@@ -50,90 +79,87 @@ func InitTriggerConfig() {
 			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "中的Rule方法失败。", err)
 			continue
 		}
-		// 判断topic
-		// todo 如果是未知的topic,可以添加一个循环,更新平台配置,同理金龙车和多功能车
+
 		if TopicOfDiagnostics == topic2 { // 1
-			f, ok := rule.(func(*diagnostic_msgs.DiagnosticArray) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *diagnostic_msgs.DiagnosticArray) string):", err)
-				continue
+			if f, ok1 := rule.(func(*diagnostic_msgs.DiagnosticArray) string); ok1 {
+				RuleOfDiagnostics = append(RuleOfDiagnostics, f)
+				goto JudgeDone
 			}
-			RuleOfDiagnostics = append(RuleOfDiagnostics, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfImu == topic2 { // 2
-			f, ok := rule.(func(data *sensor_msgs.Imu) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *sensor_msgs.Imu) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *sensor_msgs.Imu) string); ok1 {
+				RuleOfImu = append(RuleOfImu, f)
+				goto JudgeDone
 			}
-			RuleOfImu = append(RuleOfImu, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfLocateInfo == topic2 { // 3
-			f, ok := rule.(func(data *pjibot_delivery_msgs.LocateInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.LocateInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *pjibot_delivery_msgs.LocateInfo) string); ok1 {
+				RuleOfLocateInfo = append(RuleOfLocateInfo, f)
+				goto JudgeDone
 			}
-			RuleOfLocateInfo = append(RuleOfLocateInfo, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfObstacleDetection == topic2 { // 4
-			f, ok := rule.(func(data *std_msgs.UInt8) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *std_msgs.UInt8) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *std_msgs.UInt8) string); ok1 {
+				RuleOfObstacleDetection = append(RuleOfObstacleDetection, f)
+				goto JudgeDone
 			}
-			RuleOfObstacleDetection = append(RuleOfObstacleDetection, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfOdom == topic2 { // 5
-			f, ok := rule.(func(data *nav_msgs.Odometry) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *nav_msgs.Odometry) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *nav_msgs.Odometry) string); ok1 {
+				RuleOfOdom = append(RuleOfOdom, f)
+				goto JudgeDone
 			}
-			RuleOfOdom = append(RuleOfOdom, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfSysInfo == topic2 { // 6
-			f, ok := rule.(func(data *pjibot_delivery_msgs.SysInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.SysInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *pjibot_delivery_msgs.SysInfo) string); ok1 {
+				RuleOfSysInfo = append(RuleOfSysInfo, f)
+				goto JudgeDone
 			}
-			RuleOfSysInfo = append(RuleOfSysInfo, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfRobotPose == topic2 { // 7
-			f, ok := rule.(func(data *geometry_msgs.PoseStamped) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.SysInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *geometry_msgs.PoseStamped) string); ok1 {
+				RuleOfRobotPose = append(RuleOfRobotPose, f)
+				goto JudgeDone
 			}
-			RuleOfRobotPose = append(RuleOfRobotPose, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfTaskFeedbackInfo == topic2 { // 8
-			f, ok := rule.(func(data *pjibot_delivery_msgs.TaskFeedbackInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pjibot_delivery_msgs.TaskFeedbackInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *pjibot_delivery_msgs.TaskFeedbackInfo) string); ok1 {
+				RuleOfTaskFeedbackInfo = append(RuleOfTaskFeedbackInfo, f)
+				goto JudgeDone
 			}
-			RuleOfTaskFeedbackInfo = append(RuleOfTaskFeedbackInfo, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfWheelOdom == topic2 { // 9
-			f, ok := rule.(func(data *nav_msgs.Odometry) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *nav_msgs.Odometry) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *nav_msgs.Odometry) string); ok1 {
+				RuleOfWheelOdom = append(RuleOfWheelOdom, f)
+				goto JudgeDone
 			}
-			RuleOfWheelOdom = append(RuleOfWheelOdom, f)
+			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, "中的TriggerName方法失败。", err)
-			continue
-		}
-		labelFunc, ok := label.(func() string)
-		if ok != true {
-			c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Label方法必须是(func() string):", err)
+			c_log.GlobalLogger.Error("加载本地插件 ", triggerLocalPath, " 中的 Label 方法失败。", err)
 			continue
 		}
+		labelFunc := label.(func() string)
 		labelString := labelFunc()
-		LabelMapTriggerId[labelString] = strconv.Itoa(trigger.TriggerId)
-		loadSuccess++
-		c_log.GlobalLogger.Info("主节点加载触发器插件:【ros topic】=", topic2, ",【触发器label】=", labelString, "【触发器ID】=", trigger.TriggerId)
+		LabelMapTriggerId.Store(labelString, triggerId)
+		success++
 	}
-	c_log.GlobalLogger.Infof("一共有%v个触发器,加载成功了%v个,【label和id映射关系】=%v", len(config.PlatformConfig.TaskTriggers), loadSuccess, LabelMapTriggerId)
+	c_log.GlobalLogger.Info("一共应加载", len(config.PlatformConfig.TaskTriggers), "个触发器,实际加载 ", success, " 个触发器。")
+}
+func log(triggerLocalPath string) {
+	c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的 Rule 方法参数格式不正确。")
 }

+ 17 - 1
aarch64/pjibot_delivery/master/package/config/trigger_var.go

@@ -7,10 +7,11 @@ import (
 	"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/std_msgs"
+	"sync"
 )
 
 var (
-	LabelMapTriggerId = make(map[string]string)
+	LabelMapTriggerId = new(sync.Map)
 
 	// 1
 	TopicOfDiagnostics = "/diagnostics"
@@ -39,4 +40,19 @@ var (
 	// 9
 	TopicOfWheelOdom = "/wheel_odom"
 	RuleOfWheelOdom  []func(data *nav_msgs.Odometry) string
+
+	// todo 这里是全量的topic,添加topic则需要同时在下面的数组添加;也需要在produce_window.go中添加新的订阅者
+	AllTopics = []string{
+		TopicOfDiagnostics,       // 1
+		TopicOfImu,               // 2
+		TopicOfLocateInfo,        // 3
+		TopicOfObstacleDetection, // 4
+		TopicOfOdom,              // 5
+		TopicOfSysInfo,           // 6
+		TopicOfRobotPose,         // 7
+		TopicOfTaskFeedbackInfo,  // 8
+		TopicOfWheelOdom,         // 9
+	}
+
+	AllTopicsNumber = len(AllTopics)
 )

+ 3 - 0
aarch64/pjibot_delivery/master/package/service/move_bag_and_send_window.go

@@ -56,6 +56,9 @@ func RunTimeWindowProducerQueue() {
 			}
 			// 如果没有包可以供当前窗口移动,且已经生成了更新的包,则当前窗口已经可以上传
 			if !move && bigger {
+				time.Sleep(time.Duration(2) * time.Second)
+				c_log.GlobalLogger.Info("采集数据,发送record命令进程关闭信号。")
+				commonService.ChannelKillRosRecord <- 3
 				domain.SupplyCopyBags(commonConfig.CloudConfig.BagDataDir, commonConfig.CloudConfig.BagCopyDir, currentTimeWindow)
 				// 将时间窗口移出准备队列
 				entity.RemoveHeadOfTimeWindowProducerQueue()

+ 329 - 36
aarch64/pjibot_delivery/master/package/service/produce_window.go

@@ -8,57 +8,351 @@ import (
 	"cicv-data-closedloop/common/entity"
 	"cicv-data-closedloop/common/util"
 	commonUtil "cicv-data-closedloop/common/util"
+	"cicv-data-closedloop/pjibot_delivery_msgs"
 	"encoding/json"
 	"github.com/bluenviron/goroslib/v2"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/diagnostic_msgs"
+	"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/std_msgs"
 	"sync"
 	"time"
 )
 
+var triggerInterval = 3.0 // 每个触发器3秒触发一次
+
 // PrepareTimeWindowProducerQueue 负责监听所有主题并修改时间窗口
 func PrepareTimeWindowProducerQueue() {
 
 	var err error
-	subscribers := make([]*goroslib.Subscriber, len(commonConfig.SubscribeTopics))
-	subscribersTimes := make([]time.Time, len(commonConfig.SubscribeTopics))
-	subscribersTimeMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	subscribersMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	for i, topic := range commonConfig.SubscribeTopics {
-		c_log.GlobalLogger.Info("创建订阅者订阅话题:" + topic)
-		if topic == masterConfig.TopicOfObstacleDetection && len(masterConfig.RuleOfObstacleDetection) > 0 {
-			subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
-				Node:  commonConfig.RosNode,
-				Topic: topic,
-				Callback: func(data *std_msgs.UInt8) {
-					subscribersTimeMutexes[i].Lock()
-					if time.Since(subscribersTimes[i]).Seconds() > 1 {
-						subscribersMutexes[i].Lock()
-						faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
-						lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
-						var faultLabel string
-						for _, f := range masterConfig.RuleOfObstacleDetection {
-							faultLabel = f(data)
-							if faultLabel != "" {
-								if canCollect() {
-									saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+	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 {
+			create := false // 判断是否创建成功,用于打印日志
+			// 1
+			if topic == masterConfig.TopicOfDiagnostics && len(masterConfig.RuleOfDiagnostics) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *diagnostic_msgs.DiagnosticArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfDiagnostics {
+								faultLabel = f(data)
+								if faultLabel != "" {
 									subscribersTimes[i] = time.Now()
-									break
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
 								}
 							}
+							subscribersMutexes[i].Unlock()
 						}
-						subscribersMutexes[i].Unlock()
-					}
-					subscribersTimeMutexes[i].Unlock()
-				},
-			})
-		}
-		if err != nil {
-			c_log.GlobalLogger.Info("创建订阅者", masterConfig.TopicOfObstacleDetection, "发生故障:", err)
-			//TODO 如何回传日志
-			continue
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 2
+			if topic == masterConfig.TopicOfImu && len(masterConfig.RuleOfImu) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.Imu) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfImu {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 3
+			if topic == masterConfig.TopicOfLocateInfo && len(masterConfig.RuleOfLocateInfo) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjibot_delivery_msgs.LocateInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfLocateInfo {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 4
+			if topic == masterConfig.TopicOfObstacleDetection && len(masterConfig.RuleOfObstacleDetection) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *std_msgs.UInt8) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfObstacleDetection {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 5
+			if topic == masterConfig.TopicOfOdom && len(masterConfig.RuleOfOdom) > 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 := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfOdom {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 6
+			if topic == masterConfig.TopicOfSysInfo && len(masterConfig.RuleOfSysInfo) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjibot_delivery_msgs.SysInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfSysInfo {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 7
+			if topic == masterConfig.TopicOfRobotPose && len(masterConfig.RuleOfRobotPose) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *geometry_msgs.PoseStamped) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfRobotPose {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 8
+			if topic == masterConfig.TopicOfTaskFeedbackInfo && len(masterConfig.RuleOfTaskFeedbackInfo) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjibot_delivery_msgs.TaskFeedbackInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfTaskFeedbackInfo {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 9
+			if topic == masterConfig.TopicOfWheelOdom && len(masterConfig.RuleOfWheelOdom) > 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 := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfWheelOdom {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				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 := <-commonService.ChannelKillSubscriber:
 		if signal == 1 {
@@ -148,9 +442,8 @@ func canCollect() bool {
 		return false
 	}
 	if resp.Code != 200 { // 不是200 代表不允许采集
-		c_log.GlobalLogger.Info("采集数量已超过限额,当前周期内不再采集。", resp.Code)
+		c_log.GlobalLogger.Infof("当前周期内采集数量已超过限额。%+v", resp)
 		return false
 	}
-	c_log.GlobalLogger.Info("允许采集。")
 	return true
 }

+ 0 - 0
aarch64/pjibot_delivery/common/config/sh/start-control.sh → aarch64/pjibot_delivery/start-control.sh


+ 0 - 0
aarch64/pjibot_delivery/common/config/sh/start-master.sh → aarch64/pjibot_delivery/start-master.sh


+ 1 - 1
aarch64/pjibot_delivery/common/config/yaml/配送机器人默认配置文件-cloud-config.yaml → aarch64/pjibot_delivery/配送机器人默认配置文件-cloud-config.yaml

@@ -63,7 +63,7 @@ hosts:
         - "LD_LIBRARY_PATH=/opt/ros/noetic/lib:/opt/ros/noetic/lib/aarch64-linux-gnu"
         - "ROS_ROOT=/opt/ros/noetic/share/ros"
         - "ROS_DISTRO=noetic"
-    topics:
+    topics: # /robot_pose,/robot/realtime_cost_map_,/tracking/objects,/robot/TaskInfo,/robot/targetposition,/wheel,/wheel_odom,/robot/global_trajectory_,/robot/target_trajectories,/robot/evaluator_trajectories,/robot/final_trajectory,/nav/task_feedback_info,/cmd_vel,/imu,/points_cluster,/nav/task_feedback_info
       - /robot_pose # /location_realtime
       - /robot/realtime_cost_map_ # /cloud_to_map_node
       - /tracking/objects # /tracking/imm_ukf_pda_track

+ 0 - 0
aarch64/pjibot_delivery/common/config/yaml/配送机器人默认配置文件-local-config.yaml → aarch64/pjibot_delivery/配送机器人默认配置文件-local-config.yaml


+ 0 - 33
aarch64/pjibot_guide/README.md

@@ -1,33 +0,0 @@
-# 一、由于机器人由启用单个相机变成两个相机,所以话题有多变化
-1、只启用了单个相机的机器
-/amcl_pose
-/camera/color/image_raw
-/camera/depth/points
-/diagnostics
-/locate_info
-/obstacle_detection
-/odom
-/move_base/global_costmap/costmap
-/move_base/global_costmap/costmap_updates     
-/move_base/local_costmap/costmap
-/move_base/local_costmap/costmap_updates
-/scan
-/scan_map_icp_amcl_node/scan_point_transformed
-/sys_info
-# 2、启用了两个相机的机器
-/amcl_pose
-/ob_camera_01/color/image_raw(上边的摄像头)
-/ob_camera_01/depth/points(上边的摄像头)
-/ob_camera_02/color/image_raw(下边的摄像头)
-/ob_camera_02/depth/points(下边的摄像头)
-/diagnostics
-/locate_info
-/obstacle_detection(有引导任务的时候才会有)
-/odom
-/move_base/global_costmap/costmap
-/move_base/global_costmap/costmap_updates     
-/move_base/local_costmap/costmap
-/move_base/local_costmap/costmap_updates
-/scan
-/scan_map_icp_amcl_node/scan_point_transformed
-/sys_info

+ 5 - 5
aarch64/pjibot_guide/common/config/c_platform.go

@@ -37,8 +37,8 @@ type response struct {
 }
 
 var (
-	PlatformConfig  PlatformConfigStruct
-	SubscribeTopics []string
+	PlatformConfig PlatformConfigStruct
+	RecordTopics   []string
 )
 
 // InitPlatformConfig 初始化数据闭环平台的配置
@@ -55,10 +55,10 @@ func InitPlatformConfig() {
 			continue
 		}
 		if checkPlatformConfig() {
-			SubscribeTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
+			RecordTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
 			// 去掉首尾空格
-			for i, topic := range SubscribeTopics {
-				SubscribeTopics[i] = strings.TrimSpace(topic)
+			for i, topic := range RecordTopics {
+				RecordTopics[i] = strings.TrimSpace(topic)
 			}
 			break
 		}

+ 2 - 1
aarch64/pjibot_guide/common/service/disk_clean.go

@@ -79,7 +79,8 @@ func getIndexToRemoveForLRU() int {
 	for i >= 0 {
 		for i2, window := range entity.TimeWindowConsumerQueue {
 			for _, label := range window.Labels {
-				if masterConfig.LabelMapTriggerId[label] == lru[i] {
+				value, _ := masterConfig.LabelMapTriggerId.Load(label)
+				if value == lru[i] {
 					return i2
 				}
 			}

+ 37 - 33
aarch64/pjibot_guide/common/service/rosbag_record.go

@@ -10,11 +10,12 @@ import (
 	"time"
 )
 
-// BagRecord 打包rosbag
+// 打包rosbag
 func BagRecord(nodeName string) {
 	var err error
 	c_log.GlobalLogger.Info("rosbag record goroutine - 启动")
 	for {
+	startRecord:
 		c_log.GlobalLogger.Info("校验必需的 rosnode 是否全部启动。")
 		canRecord := false
 		for !canRecord {
@@ -29,8 +30,13 @@ func BagRecord(nodeName string) {
 		command = append(command, "--duration=1")
 		for _, host := range config.CloudConfig.Hosts {
 			if host.Name == nodeName {
-				for _, topic := range host.Topics {
-					command = append(command, topic)
+				// 配置文件中的是node1和node2各自的所有topic,platformConfig中配置用户想缓存的topic,取交集
+				for _, topic1 := range config.RecordTopics {
+					for _, topic2 := range host.Topics {
+						if topic1 == topic2 {
+							command = append(command, topic1)
+						}
+					}
 				}
 			}
 		}
@@ -42,8 +48,8 @@ func BagRecord(nodeName string) {
 		var recordProcessPid int
 		var recordSubProcessPid int
 		var cmd *exec.Cmd
-		systemEnv := os.Environ()
-		c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
+		//systemEnv := os.Environ()
+		//c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
 	parent:
 		for {
 			c_log.GlobalLogger.Info("record 环境变量为:", config.RosbagEnvs)
@@ -71,39 +77,37 @@ func BagRecord(nodeName string) {
 				}
 			}
 		}
-		// 等待自杀信号
-		c_log.GlobalLogger.Info("启动record命令成功。等待自杀信号。")
-		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命令一直录包占满存储
+		// 等待进程关闭信号
+		c_log.GlobalLogger.Info("启动record命令成功。等待进程关闭信号。")
+		for {
+			select {
+			case signal := <-ChannelKillRosRecord:
+				if signal == 1 {
+					kill(recordProcessPid, recordSubProcessPid, cmd)
+					AddKillTimes("1")
+					continue // continue 是为了等待重启信号
+				}
+				if signal == 2 {
+					goto startRecord // 收到信号 2 重新 record 命令
 				}
-				if err = cmd.Process.Kill(); err != nil {
-					c_log.GlobalLogger.Error("程序阻塞,杀死record命令进程", recordProcessPid, "出错:", err)
-					select {} // 此处阻塞防止record命令一直录包占满存储
+				if signal == 3 { // 这个关闭是等待数据处理时的关闭
+					c_log.GlobalLogger.Error("采集数据,接收record命令进程关闭信号:", signal)
+					kill(recordProcessPid, recordSubProcessPid, cmd)
+					continue
 				}
-				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 kill(recordProcessPid int, recordSubProcessPid int, cmd *exec.Cmd) {
+	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命令一直录包占满存储
 	}
 }
 

+ 7 - 1
aarch64/pjibot_guide/common/service/rosbag_upload.go

@@ -124,7 +124,9 @@ outLoop:
 		// 在上传完成的包目录同级下添加一个目录同名的json
 		triggerIds := make([]string, 0)
 		for _, label := range currentTimeWindow.Labels {
-			triggerIds = append(triggerIds, masterConfig.LabelMapTriggerId[label])
+			if value, ok := masterConfig.LabelMapTriggerId.Load(label); ok {
+				triggerIds = append(triggerIds, value.(string))
+			}
 		}
 		callBackMap := map[string]interface{}{
 			"dataName":    currentTimeWindow.FaultTime, // 云端callback程序会将该值加8小时,因为UTC和CSV时区相差8小时
@@ -171,5 +173,9 @@ outLoop:
 		if err = util.RemoveDir(dir); err != nil {
 			continue outLoop
 		}
+		if len(entity.TimeWindowConsumerQueue) == 0 {
+			c_log.GlobalLogger.Infof("已处理所有窗口,重启 record 命令。")
+			ChannelKillRosRecord <- 2
+		}
 	}
 }

+ 7 - 1
aarch64/pjibot_guide/control/main.go

@@ -28,12 +28,18 @@ func init() {
 }
 
 func main() {
+	init := true
 	turnLength := 60
 	lastStatus := "NONE"
 	//  轮询任务接口判断是否有更新
 	for {
 		c_log.GlobalLogger.Errorf("一轮次扫描时间【%v】秒:", turnLength)
-		time.Sleep(time.Duration(turnLength) * time.Second)
+		if init {
+			time.Sleep(time.Duration(1) * time.Second)
+			init = false
+		} else {
+			time.Sleep(time.Duration(turnLength) * time.Second)
+		}
 		// 1 获取当前设备的任务的 status
 		status, err := commonConfig.GetStatus(commonConfig.PlatformConfig.TaskConfigId)
 		if err != nil {

+ 0 - 140
aarch64/pjibot_guide/master/package/config/master_trigger_cfg.go

@@ -1,140 +0,0 @@
-package config
-
-import (
-	"cicv-data-closedloop/aarch64/pjibot_guide/common/config"
-	"cicv-data-closedloop/common/config/c_log"
-	"cicv-data-closedloop/common/util"
-	pji_msgs "cicv-data-closedloop/pjibot_guide_msgs"
-	"github.com/bluenviron/goroslib/v2/pkg/msgs/diagnostic_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/std_msgs"
-	"plugin"
-	"strconv"
-)
-
-var (
-	LabelMapTriggerId = make(map[string]string)
-
-	// 1
-	TopicOfDiagnostics = "/diagnostics"
-	RuleOfDiagnostics  []func(data *diagnostic_msgs.DiagnosticArray) string
-	// 2
-	TopicOfImu = "/imu"
-	RuleOfImu  []func(data *sensor_msgs.Imu) string
-	// 3
-	TopicOfLocateInfo = "/locate_info"
-	RuleOfLocateInfo  []func(data *pji_msgs.LocateInfo) string
-	// 4
-	TopicOfObstacleDetection = "/obstacle_detection"
-	RuleOfObstacleDetection  []func(data *std_msgs.UInt8) string
-	// 5
-	TopicOfOdom = "/odom"
-	RuleOfOdom  []func(data *nav_msgs.Odometry) string
-	// 6
-	TopicOfSysInfo = "/sys_info"
-	RuleOfSysInfo  []func(data *pji_msgs.SysInfo) string
-)
-
-func InitTriggerConfig() {
-	loadSuccess := 0
-	// 下载所有触发器的文件
-	for _, trigger := range config.PlatformConfig.TaskTriggers {
-		// 下载
-		triggerLocalPath := config.CloudConfig.TriggersDir + trigger.TriggerScriptPath
-		_ = util.CreateParentDir(triggerLocalPath)
-		c_log.GlobalLogger.Info("下载触发器插件从", trigger.TriggerScriptPath, "到", triggerLocalPath)
-		config.OssMutex.Lock()
-		err := config.OssBucket.GetObjectToFile(trigger.TriggerScriptPath, triggerLocalPath)
-		config.OssMutex.Unlock()
-		if err != nil {
-			c_log.GlobalLogger.Errorf("下载oss上的触发器插件失败【%v】->【%v】:%v", trigger.TriggerScriptPath, triggerLocalPath, err)
-			continue
-		}
-		// 载入插件到数组
-		open, err := plugin.Open(triggerLocalPath)
-		if err != nil {
-			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "失败。", err)
-		}
-		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
-		}
-		// 判断topic
-		// todo 如果是未知的topic,可以添加一个循环,更新平台配置,同理金龙车和多功能车
-		if TopicOfDiagnostics == topic2 { // 1
-			f, ok := rule.(func(*diagnostic_msgs.DiagnosticArray) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *diagnostic_msgs.DiagnosticArray) string):", err)
-				continue
-			}
-			RuleOfDiagnostics = append(RuleOfDiagnostics, f)
-		} else if TopicOfImu == topic2 { // 2
-			f, ok := rule.(func(data *sensor_msgs.Imu) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *sensor_msgs.Imu) string):", err)
-				continue
-			}
-			RuleOfImu = append(RuleOfImu, f)
-		} else if TopicOfLocateInfo == topic2 { // 3
-			f, ok := rule.(func(data *pji_msgs.LocateInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.LocateInfo) string):", err)
-				continue
-			}
-			RuleOfLocateInfo = append(RuleOfLocateInfo, f)
-		} else if TopicOfObstacleDetection == topic2 { // 4
-			f, ok := rule.(func(data *std_msgs.UInt8) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *std_msgs.UInt8) string):", err)
-				continue
-			}
-			RuleOfObstacleDetection = append(RuleOfObstacleDetection, f)
-		} else if TopicOfOdom == topic2 { // 5
-			f, ok := rule.(func(data *nav_msgs.Odometry) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *nav_msgs.Odometry) string):", err)
-				continue
-			}
-			RuleOfOdom = append(RuleOfOdom, f)
-		} else if TopicOfSysInfo == topic2 { // 6
-			f, ok := rule.(func(data *pji_msgs.SysInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.SysInfo) string):", err)
-				continue
-			}
-			RuleOfSysInfo = append(RuleOfSysInfo, f)
-		} else {
-			c_log.GlobalLogger.Error("未知的topic:", topic2)
-			continue
-		}
-
-		label, err := open.Lookup("Label")
-		if err != nil {
-			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "中的TriggerName方法失败。", err)
-			continue
-		}
-		labelFunc, ok := label.(func() string)
-		if ok != true {
-			c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Label方法必须是(func() string):", err)
-			continue
-		}
-		labelString := labelFunc()
-		LabelMapTriggerId[labelString] = strconv.Itoa(trigger.TriggerId)
-		loadSuccess++
-		c_log.GlobalLogger.Info("主节点加载触发器插件:【ros topic】=", topic2, ",【触发器label】=", labelString, "【触发器ID】=", trigger.TriggerId)
-	}
-	c_log.GlobalLogger.Infof("一共有%v个触发器,加载成功了%v个,【label和id映射关系】=%v", len(config.PlatformConfig.TaskTriggers), loadSuccess, LabelMapTriggerId)
-}

+ 142 - 0
aarch64/pjibot_guide/master/package/config/trigger_init.go

@@ -0,0 +1,142 @@
+package config
+
+import (
+	"cicv-data-closedloop/aarch64/pjibot_guide/common/config"
+	"cicv-data-closedloop/common/config/c_log"
+	"cicv-data-closedloop/common/util"
+	"cicv-data-closedloop/pjibot_guide_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/diagnostic_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/std_msgs"
+	"plugin"
+	"slices"
+)
+
+func InitTriggerConfig() {
+	config.OssMutex.Lock()
+	defer config.OssMutex.Unlock()
+	triggerLocalPathsMapTriggerId := make(map[string]string)
+	c_log.GlobalLogger.Info("主节点加载触发器插件 - 开始。")
+	// 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
+	}
+
+	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 TopicOfDiagnostics == topic2 { // 1
+			if f, ok1 := rule.(func(*diagnostic_msgs.DiagnosticArray) string); ok1 {
+				RuleOfDiagnostics = append(RuleOfDiagnostics, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfImu == topic2 { // 2
+			if f, ok1 := rule.(func(data *sensor_msgs.Imu) string); ok1 {
+				RuleOfImu = append(RuleOfImu, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfLocateInfo == topic2 { // 3
+			if f, ok1 := rule.(func(data *pjibot_guide_msgs.LocateInfo) string); ok1 {
+				RuleOfLocateInfo = append(RuleOfLocateInfo, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfObstacleDetection == topic2 { // 4
+			if f, ok1 := rule.(func(data *std_msgs.UInt8) string); ok1 {
+				RuleOfObstacleDetection = append(RuleOfObstacleDetection, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfOdom == topic2 { // 5
+			if f, ok1 := rule.(func(data *nav_msgs.Odometry) string); ok1 {
+				RuleOfOdom = append(RuleOfOdom, f)
+				goto JudgeDone
+			}
+			log(triggerLocalPath)
+			continue
+		} else if TopicOfSysInfo == topic2 { // 6
+			if f, ok1 := rule.(func(data *pjibot_guide_msgs.SysInfo) string); ok1 {
+				RuleOfSysInfo = append(RuleOfSysInfo, 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(config.PlatformConfig.TaskTriggers), "个触发器,实际加载 ", success, " 个触发器。")
+}
+func log(triggerLocalPath string) {
+	c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的 Rule 方法参数格式不正确。")
+}

+ 43 - 0
aarch64/pjibot_guide/master/package/config/trigger_var.go

@@ -0,0 +1,43 @@
+package config
+
+import (
+	"cicv-data-closedloop/pjibot_guide_msgs"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/diagnostic_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/std_msgs"
+	"sync"
+)
+
+var (
+	LabelMapTriggerId = new(sync.Map)
+
+	// 1
+	TopicOfDiagnostics = "/diagnostics"
+	RuleOfDiagnostics  []func(data *diagnostic_msgs.DiagnosticArray) string
+	// 2
+	TopicOfImu = "/imu"
+	RuleOfImu  []func(data *sensor_msgs.Imu) string
+	// 3
+	TopicOfLocateInfo = "/locate_info"
+	RuleOfLocateInfo  []func(data *pjibot_guide_msgs.LocateInfo) string
+	// 4
+	TopicOfObstacleDetection = "/obstacle_detection"
+	RuleOfObstacleDetection  []func(data *std_msgs.UInt8) string
+	// 5
+	TopicOfOdom = "/odom"
+	RuleOfOdom  []func(data *nav_msgs.Odometry) string
+	// 6
+	TopicOfSysInfo = "/sys_info"
+	RuleOfSysInfo  []func(data *pjibot_guide_msgs.SysInfo) string
+	// todo 这里是全量的topic,添加topic则需要同时在下面的数组添加;也需要在produce_window.go中添加新的订阅者
+	AllTopics = []string{
+		TopicOfDiagnostics,       // 1
+		TopicOfImu,               // 2
+		TopicOfLocateInfo,        // 3
+		TopicOfObstacleDetection, // 4
+		TopicOfOdom,              // 5
+		TopicOfSysInfo,           // 6
+	}
+	AllTopicsNumber = len(AllTopics)
+)

+ 4 - 0
aarch64/pjibot_guide/master/package/service/move_bag_and_send_window.go

@@ -33,6 +33,7 @@ func RunTimeWindowProducerQueue() {
 		// 处理
 		time.Sleep(time.Duration(1) * time.Second)
 		if len(entity.TimeWindowProducerQueue) > 0 {
+			// 处理
 			bags, _ := util.ListAbsolutePathWithSuffixAndSort(commonConfig.CloudConfig.BagDataDir, ".bag")
 			currentTimeWindow := entity.TimeWindowProducerQueue[0]
 			move := false
@@ -56,6 +57,9 @@ func RunTimeWindowProducerQueue() {
 			}
 			// 如果没有包可以供当前窗口移动,且已经生成了更新的包,则当前窗口已经可以上传
 			if !move && bigger {
+				time.Sleep(time.Duration(2) * time.Second)
+				c_log.GlobalLogger.Info("采集数据,发送record命令进程关闭信号。")
+				commonService.ChannelKillRosRecord <- 3
 				domain.SupplyCopyBags(commonConfig.CloudConfig.BagDataDir, commonConfig.CloudConfig.BagCopyDir, currentTimeWindow)
 				// 将时间窗口移出准备队列
 				entity.RemoveHeadOfTimeWindowProducerQueue()

+ 49 - 35
aarch64/pjibot_guide/master/package/service/produce_window.go

@@ -15,50 +15,64 @@ import (
 	"time"
 )
 
-// PrepareTimeWindowProducerQueue 负责监听所有主题并修改时间窗口
+var triggerInterval = 3.0 // 每个触发器3秒触发一次
+// 负责监听所有主题并修改时间窗口
 func PrepareTimeWindowProducerQueue() {
 
 	var err error
-	subscribers := make([]*goroslib.Subscriber, len(commonConfig.SubscribeTopics))
-	subscribersTimes := make([]time.Time, len(commonConfig.SubscribeTopics))
-	subscribersTimeMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	subscribersMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	for i, topic := range commonConfig.SubscribeTopics {
-		c_log.GlobalLogger.Info("创建订阅者订阅话题:" + topic)
-		if topic == masterConfig.TopicOfObstacleDetection && len(masterConfig.RuleOfObstacleDetection) > 0 {
-			subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
-				Node:  commonConfig.RosNode,
-				Topic: topic,
-				Callback: func(data *std_msgs.UInt8) {
-					subscribersTimeMutexes[i].Lock()
-					if time.Since(subscribersTimes[i]).Seconds() > 1 {
-						subscribersMutexes[i].Lock()
-						faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
-						lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
-						var faultLabel string
-						for _, f := range masterConfig.RuleOfObstacleDetection {
-							faultLabel = f(data)
-							if faultLabel != "" {
-								if canCollect() {
-									saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+	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 {
+			create := false // 判断是否创建成功,用于打印日志
+			if topic == masterConfig.TopicOfObstacleDetection && len(masterConfig.RuleOfObstacleDetection) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *std_msgs.UInt8) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfObstacleDetection {
+								faultLabel = f(data)
+								if faultLabel != "" {
 									subscribersTimes[i] = time.Now()
-									break
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
 								}
 							}
+							subscribersMutexes[i].Unlock()
 						}
-						subscribersMutexes[i].Unlock()
-					}
-					subscribersTimeMutexes[i].Unlock()
-				},
-			})
-		}
-		if err != nil {
-			c_log.GlobalLogger.Info("创建订阅者", masterConfig.TopicOfObstacleDetection, "发生故障:", err)
-			//TODO 如何回传日志
-			continue
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				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 := <-commonService.ChannelKillSubscriber:
 		if signal == 1 {

+ 0 - 0
aarch64/pjibot_guide/common/config/sh/start-control.sh → aarch64/pjibot_guide/start-control.sh


+ 0 - 0
aarch64/pjibot_guide/common/config/sh/start-master.sh → aarch64/pjibot_guide/start-master.sh


+ 1 - 1
aarch64/pjibot_guide/common/config/yaml/引导机器人默认配置文件-cloud-config.yaml → aarch64/pjibot_guide/引导机器人默认配置文件-cloud-config.yaml

@@ -82,7 +82,7 @@ hosts:
         - "PYTHONPATH=/opt/ros/melodic/lib/python2.7/dist-packages"
         - "ROS_HOSTNAME=192.168.1.104"
         - "CMAKE_PREFIX_PATH=/opt/ros/melodic"
-    topics:
+    topics: # /amcl_pose,/ob_camera_01/color/image_raw,/ob_camera_02/color/image_raw,/diagnostics,/locate_info,/obstacle_detection,/odom,/move_base/global_costmap/costmap,/move_base/global_costmap/costmap_updates,/move_base/local_costmap/costmap,/move_base/local_costmap/costmap_updates,/scan,/scan_map_icp_amcl_node/scan_point_transformed,/sys_info,/imu,/depth_scan_02,/map,/scan_filtered,/sonar_left,/sonar_right,/sonar_mid,/sonar_rmid,/tf,/tf_static,/cmd_vel,/move_base/DWAPlannerROS/global_plan,/move_base/DWAPlannerROS/local_plan,/move_base/GlobalPlanner/plan,/move_base/global_costmap/footprint,/move_base/local_costmap/footprint,/robot_pose_tf
       - /amcl_pose # /amcl
       - /ob_camera_01/color/image_raw # /ob_camera_01/camera
       #      - /ob_camera_01/depth/points # /ob_camera_01/camera

+ 0 - 0
aarch64/pjibot_guide/common/config/yaml/引导机器人默认配置文件-local-config.yaml → aarch64/pjibot_guide/引导机器人默认配置文件-local-config.yaml


+ 0 - 33
aarch64/pjibot_patrol/README.md

@@ -1,33 +0,0 @@
-# 一、由于机器人由启用单个相机变成两个相机,所以话题有多变化
-1、只启用了单个相机的机器
-/amcl_pose
-/camera/color/image_raw
-/camera/depth/points
-/diagnostics
-/locate_info
-/obstacle_detection
-/odom
-/move_base/global_costmap/costmap
-/move_base/global_costmap/costmap_updates     
-/move_base/local_costmap/costmap
-/move_base/local_costmap/costmap_updates
-/scan
-/scan_map_icp_amcl_node/scan_point_transformed
-/sys_info
-# 2、启用了两个相机的机器
-/amcl_pose
-/ob_camera_01/color/image_raw(上边的摄像头)
-/ob_camera_01/depth/points(上边的摄像头)
-/ob_camera_02/color/image_raw(下边的摄像头)
-/ob_camera_02/depth/points(下边的摄像头)
-/diagnostics
-/locate_info
-/obstacle_detection(有引导任务的时候才会有)
-/odom
-/move_base/global_costmap/costmap
-/move_base/global_costmap/costmap_updates     
-/move_base/local_costmap/costmap
-/move_base/local_costmap/costmap_updates
-/scan
-/scan_map_icp_amcl_node/scan_point_transformed
-/sys_info

+ 6 - 6
aarch64/pjibot_patrol/common/config/c_platform.go

@@ -37,11 +37,11 @@ type response struct {
 }
 
 var (
-	PlatformConfig  PlatformConfigStruct
-	SubscribeTopics []string
+	PlatformConfig PlatformConfigStruct
+	RecordTopics   []string
 )
 
-// InitPlatformConfig 初始化数据闭环平台的配置
+// 初始化数据闭环平台的配置
 func InitPlatformConfig() {
 	var err error
 	c_log.GlobalLogger.Info("获取数据闭环平台配置 - 开始")
@@ -55,10 +55,10 @@ func InitPlatformConfig() {
 			continue
 		}
 		if checkPlatformConfig() {
-			SubscribeTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
+			RecordTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
 			// 去掉首尾空格
-			for i, topic := range SubscribeTopics {
-				SubscribeTopics[i] = strings.TrimSpace(topic)
+			for i, topic := range RecordTopics {
+				RecordTopics[i] = strings.TrimSpace(topic)
 			}
 			break
 		}

+ 4 - 2
aarch64/pjibot_patrol/common/service/disk_clean.go

@@ -79,8 +79,10 @@ func getIndexToRemoveForLRU() int {
 	for i >= 0 {
 		for i2, window := range entity.TimeWindowConsumerQueue {
 			for _, label := range window.Labels {
-				if masterConfig.LabelMapTriggerId[label] == lru[i] {
-					return i2
+				if value, ok := masterConfig.LabelMapTriggerId.Load(label); ok {
+					if value == lru[i] {
+						return i2
+					}
 				}
 			}
 		}

+ 34 - 32
aarch64/pjibot_patrol/common/service/rosbag_record.go

@@ -15,6 +15,7 @@ func BagRecord(nodeName string) {
 	var err error
 	c_log.GlobalLogger.Info("rosbag record goroutine - 启动")
 	for {
+	startRecord:
 		c_log.GlobalLogger.Info("校验必需的 rosnode 是否全部启动。")
 		canRecord := false
 		for !canRecord {
@@ -29,8 +30,13 @@ func BagRecord(nodeName string) {
 		command = append(command, "--duration=1")
 		for _, host := range config.CloudConfig.Hosts {
 			if host.Name == nodeName {
-				for _, topic := range host.Topics {
-					command = append(command, topic)
+				// 配置文件中的是node1和node2各自的所有topic,platformConfig中配置用户想缓存的topic,取交集
+				for _, topic1 := range config.RecordTopics {
+					for _, topic2 := range host.Topics {
+						if topic1 == topic2 {
+							command = append(command, topic1)
+						}
+					}
 				}
 			}
 		}
@@ -42,8 +48,8 @@ func BagRecord(nodeName string) {
 		var recordProcessPid int
 		var recordSubProcessPid int
 		var cmd *exec.Cmd
-		systemEnv := os.Environ()
-		c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
+		//systemEnv := os.Environ()
+		//c_log.GlobalLogger.Info("系统环境变量为:", systemEnv)
 	parent:
 		for {
 			c_log.GlobalLogger.Info("record 环境变量为:", config.RosbagEnvs)
@@ -73,40 +79,36 @@ func BagRecord(nodeName string) {
 		}
 		// 等待自杀信号
 		c_log.GlobalLogger.Info("启动record命令成功。等待自杀信号。")
-		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命令一直录包占满存储
+		for {
+			select {
+			case signal := <-ChannelKillRosRecord:
+				if signal == 1 {
+					kill(recordProcessPid, recordSubProcessPid, cmd)
+					AddKillTimes("1")
+					continue // continue 是为了等待重启信号
+				}
+				if signal == 2 {
+					goto startRecord // 收到信号 2 重新 record 命令
 				}
-				if err = cmd.Process.Kill(); err != nil {
-					c_log.GlobalLogger.Error("程序阻塞,杀死record命令进程", recordProcessPid, "出错:", err)
-					select {} // 此处阻塞防止record命令一直录包占满存储
+				if signal == 3 { // 这个关闭是等待数据处理时的关闭
+					c_log.GlobalLogger.Error("采集数据,接收record命令进程关闭信号:", signal)
+					kill(recordProcessPid, recordSubProcessPid, cmd)
+					continue
 				}
-				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 kill(recordProcessPid int, recordSubProcessPid int, cmd *exec.Cmd) {
+	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命令一直录包占满存储
+	}
+}
 func isCanRecord(n *goroslib.Node) bool {
 	time.Sleep(time.Duration(1) * time.Second)
 	// 获取

+ 7 - 1
aarch64/pjibot_patrol/common/service/rosbag_upload.go

@@ -123,7 +123,9 @@ outLoop:
 		// 在上传完成的包目录同级下添加一个目录同名的json
 		triggerIds := make([]string, 0)
 		for _, label := range currentTimeWindow.Labels {
-			triggerIds = append(triggerIds, masterConfig.LabelMapTriggerId[label])
+			if value, ok := masterConfig.LabelMapTriggerId.Load(label); ok {
+				triggerIds = append(triggerIds, value.(string))
+			}
 		}
 		callBackMap := map[string]interface{}{
 			"dataName":    currentTimeWindow.FaultTime, // 云端callback程序会将该值加8小时,因为UTC和CSV时区相差8小时
@@ -152,6 +154,10 @@ outLoop:
 		if err = util.RemoveDir(dir); err != nil {
 			continue outLoop
 		}
+		if len(entity.TimeWindowConsumerQueue) == 0 {
+			c_log.GlobalLogger.Infof("已处理所有窗口,重启 record 命令。")
+			ChannelKillRosRecord <- 2
+		}
 
 	}
 }

+ 9 - 1
aarch64/pjibot_patrol/control/main.go

@@ -28,10 +28,18 @@ func init() {
 }
 
 func main() {
+	init := true
+	turnLength := 60
 	lastStatus := "NONE"
 	//  轮询任务接口判断是否有更新
 	for {
-		time.Sleep(time.Duration(60) * time.Second)
+		c_log.GlobalLogger.Errorf("一轮次扫描时间【%v】秒:", turnLength)
+		if init {
+			time.Sleep(time.Duration(1) * time.Second)
+			init = false
+		} else {
+			time.Sleep(time.Duration(turnLength) * time.Second)
+		}
 		// 1 获取当前设备的任务的 status
 		status, err := commonConfig.GetStatus(commonConfig.PlatformConfig.TaskConfigId)
 		if err != nil {

+ 97 - 70
aarch64/pjibot_patrol/master/package/config/trigger_init.go

@@ -11,28 +11,57 @@ import (
 	"github.com/bluenviron/goroslib/v2/pkg/msgs/sensor_msgs"
 	"github.com/bluenviron/goroslib/v2/pkg/msgs/std_msgs"
 	"plugin"
-	"strconv"
+	"slices"
 )
 
 func InitTriggerConfig() {
-	loadSuccess := 0
-	// 下载所有触发器的文件
-	for _, trigger := range config.PlatformConfig.TaskTriggers {
-		// 下载
-		triggerLocalPath := config.CloudConfig.TriggersDir + trigger.TriggerScriptPath
+	config.OssMutex.Lock()
+	defer config.OssMutex.Unlock()
+	triggerLocalPathsMapTriggerId := make(map[string]string)
+	c_log.GlobalLogger.Info("主节点加载触发器插件 - 开始。")
+	// 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)
-		c_log.GlobalLogger.Info("下载触发器插件从", trigger.TriggerScriptPath, "到", triggerLocalPath)
-		config.OssMutex.Lock()
-		err := config.OssBucket.GetObjectToFile(trigger.TriggerScriptPath, triggerLocalPath)
-		config.OssMutex.Unlock()
-		if err != nil {
-			c_log.GlobalLogger.Errorf("下载oss上的触发器插件失败【%v】->【%v】:%v", trigger.TriggerScriptPath, triggerLocalPath, err)
-			continue
+		for {
+			if err := config.OssBucket.GetObjectToFile(trigger.TriggerScriptPath, triggerLocalPath); err != nil {
+				c_log.GlobalLogger.Error("下载触发器插件失败,再次尝试:", err)
+				continue
+			}
+			break
 		}
+		triggerLocalPathsMapTriggerId[triggerLocalPath] = id
+	}
+
+	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 {
@@ -50,90 +79,88 @@ func InitTriggerConfig() {
 			c_log.GlobalLogger.Error("加载本地插件", triggerLocalPath, "中的Rule方法失败。", err)
 			continue
 		}
-		// 判断topic
-		// todo 如果是未知的topic,可以添加一个循环,更新平台配置,同理金龙车和多功能车
+
 		if TopicOfDiagnostics == topic2 { // 1
-			f, ok := rule.(func(*diagnostic_msgs.DiagnosticArray) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *diagnostic_msgs.DiagnosticArray) string):", err)
-				continue
+			if f, ok1 := rule.(func(*diagnostic_msgs.DiagnosticArray) string); ok1 {
+				RuleOfDiagnostics = append(RuleOfDiagnostics, f)
+				goto JudgeDone
 			}
-			RuleOfDiagnostics = append(RuleOfDiagnostics, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfImu == topic2 { // 2
-			f, ok := rule.(func(data *sensor_msgs.Imu) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *sensor_msgs.Imu) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *sensor_msgs.Imu) string); ok1 {
+				RuleOfImu = append(RuleOfImu, f)
+				goto JudgeDone
 			}
-			RuleOfImu = append(RuleOfImu, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfLocateInfo == topic2 { // 3
-			f, ok := rule.(func(data *pjibot_patrol_msgs.LocateInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.LocateInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *pjibot_patrol_msgs.LocateInfo) string); ok1 {
+				RuleOfLocateInfo = append(RuleOfLocateInfo, f)
+				goto JudgeDone
 			}
-			RuleOfLocateInfo = append(RuleOfLocateInfo, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfObstacleDetection == topic2 { // 4
-			f, ok := rule.(func(data *std_msgs.UInt8) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *std_msgs.UInt8) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *std_msgs.UInt8) string); ok1 {
+				RuleOfObstacleDetection = append(RuleOfObstacleDetection, f)
+				goto JudgeDone
 			}
-			RuleOfObstacleDetection = append(RuleOfObstacleDetection, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfOdom == topic2 { // 5
-			f, ok := rule.(func(data *nav_msgs.Odometry) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *nav_msgs.Odometry) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *nav_msgs.Odometry) string); ok1 {
+				RuleOfOdom = append(RuleOfOdom, f)
+				goto JudgeDone
 			}
-			RuleOfOdom = append(RuleOfOdom, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfSysInfo == topic2 { // 6
-			f, ok := rule.(func(data *pjibot_patrol_msgs.SysInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.SysInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *pjibot_patrol_msgs.SysInfo) string); ok1 {
+				RuleOfSysInfo = append(RuleOfSysInfo, f)
+				goto JudgeDone
 			}
-			RuleOfSysInfo = append(RuleOfSysInfo, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfRobotPose == topic2 { // 7
-			f, ok := rule.(func(data *geometry_msgs.PoseStamped) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pji_msgs.SysInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *geometry_msgs.PoseStamped) string); ok1 {
+				RuleOfRobotPose = append(RuleOfRobotPose, f)
+				goto JudgeDone
 			}
-			RuleOfRobotPose = append(RuleOfRobotPose, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfTaskFeedbackInfo == topic2 { // 8
-			f, ok := rule.(func(data *pjibot_patrol_msgs.TaskFeedbackInfo) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *pjibot_patrol_msgs.TaskFeedbackInfo) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *pjibot_patrol_msgs.TaskFeedbackInfo) string); ok1 {
+				RuleOfTaskFeedbackInfo = append(RuleOfTaskFeedbackInfo, f)
+				goto JudgeDone
 			}
-			RuleOfTaskFeedbackInfo = append(RuleOfTaskFeedbackInfo, f)
+			log(triggerLocalPath)
+			continue
 		} else if TopicOfWheelOdom == topic2 { // 9
-			f, ok := rule.(func(data *nav_msgs.Odometry) string)
-			if ok != true {
-				c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Topic方法必须是(func(data *nav_msgs.Odometry) string):", err)
-				continue
+			if f, ok1 := rule.(func(data *nav_msgs.Odometry) string); ok1 {
+				RuleOfWheelOdom = append(RuleOfWheelOdom, f)
+				goto JudgeDone
 			}
-			RuleOfWheelOdom = append(RuleOfWheelOdom, f)
+			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, "中的TriggerName方法失败。", err)
-			continue
-		}
-		labelFunc, ok := label.(func() string)
-		if ok != true {
-			c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的Label方法必须是(func() string):", err)
+			c_log.GlobalLogger.Error("加载本地插件 ", triggerLocalPath, " 中的 Label 方法失败。", err)
 			continue
 		}
+		labelFunc := label.(func() string)
 		labelString := labelFunc()
-		LabelMapTriggerId[labelString] = strconv.Itoa(trigger.TriggerId)
-		loadSuccess++
-		c_log.GlobalLogger.Info("主节点加载触发器插件:【ros topic】=", topic2, ",【触发器label】=", labelString, "【触发器ID】=", trigger.TriggerId)
+		LabelMapTriggerId.Store(labelString, triggerId)
+		success++
 	}
-	c_log.GlobalLogger.Infof("一共有%v个触发器,加载成功了%v个,【label和id映射关系】=%v", len(config.PlatformConfig.TaskTriggers), loadSuccess, LabelMapTriggerId)
+	c_log.GlobalLogger.Info("一共应加载", len(config.PlatformConfig.TaskTriggers), "个触发器,实际加载 ", success, " 个触发器。")
+}
+func log(triggerLocalPath string) {
+	c_log.GlobalLogger.Error("插件", triggerLocalPath, "中的 Rule 方法参数格式不正确。")
 }

+ 16 - 2
aarch64/pjibot_patrol/master/package/config/trigger_var.go

@@ -7,11 +7,11 @@ import (
 	"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/std_msgs"
+	"sync"
 )
 
 var (
-	LabelMapTriggerId = make(map[string]string)
-
+	LabelMapTriggerId = new(sync.Map)
 	// 1
 	TopicOfDiagnostics = "/diagnostics"
 	RuleOfDiagnostics  []func(data *diagnostic_msgs.DiagnosticArray) string
@@ -39,4 +39,18 @@ var (
 	// 9
 	TopicOfWheelOdom = "/wheel_odom"
 	RuleOfWheelOdom  []func(data *nav_msgs.Odometry) string
+
+	// todo 这里是全量的topic,添加topic则需要同时在下面的数组添加;也需要在produce_window.go中添加新的订阅者
+	AllTopics = []string{
+		TopicOfDiagnostics,       // 1
+		TopicOfImu,               // 2
+		TopicOfLocateInfo,        // 3
+		TopicOfObstacleDetection, // 4
+		TopicOfOdom,              // 5
+		TopicOfSysInfo,           // 6
+		TopicOfRobotPose,         // 7
+		TopicOfTaskFeedbackInfo,  // 8
+		TopicOfWheelOdom,         // 9
+	}
+	AllTopicsNumber = len(AllTopics)
 )

+ 3 - 0
aarch64/pjibot_patrol/master/package/service/move_bag_and_send_window.go

@@ -56,6 +56,9 @@ func RunTimeWindowProducerQueue() {
 			}
 			// 如果没有包可以供当前窗口移动,且已经生成了更新的包,则当前窗口已经可以上传
 			if !move && bigger {
+				time.Sleep(time.Duration(2) * time.Second)
+				c_log.GlobalLogger.Info("采集数据,发送record命令进程关闭信号。")
+				commonService.ChannelKillRosRecord <- 3
 				domain.SupplyCopyBags(commonConfig.CloudConfig.BagDataDir, commonConfig.CloudConfig.BagCopyDir, currentTimeWindow)
 				// 将时间窗口移出准备队列
 				entity.RemoveHeadOfTimeWindowProducerQueue()

+ 329 - 37
aarch64/pjibot_patrol/master/package/service/produce_window.go

@@ -8,57 +8,350 @@ import (
 	"cicv-data-closedloop/common/entity"
 	"cicv-data-closedloop/common/util"
 	commonUtil "cicv-data-closedloop/common/util"
+	"cicv-data-closedloop/pjibot_patrol_msgs"
 	"encoding/json"
 	"github.com/bluenviron/goroslib/v2"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/diagnostic_msgs"
+	"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/std_msgs"
 	"sync"
 	"time"
 )
 
-// PrepareTimeWindowProducerQueue 负责监听所有主题并修改时间窗口
+var triggerInterval = 3.0 // 每个触发器3秒触发一次
+// 负责监听所有主题并修改时间窗口
 func PrepareTimeWindowProducerQueue() {
 
 	var err error
-	subscribers := make([]*goroslib.Subscriber, len(commonConfig.SubscribeTopics))
-	subscribersTimes := make([]time.Time, len(commonConfig.SubscribeTopics))
-	subscribersTimeMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	subscribersMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	for i, topic := range commonConfig.SubscribeTopics {
-		c_log.GlobalLogger.Info("创建订阅者订阅话题:" + topic)
-		if topic == masterConfig.TopicOfObstacleDetection && len(masterConfig.RuleOfObstacleDetection) > 0 {
-			subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
-				Node:  commonConfig.RosNode,
-				Topic: topic,
-				Callback: func(data *std_msgs.UInt8) {
-					subscribersTimeMutexes[i].Lock()
-					if time.Since(subscribersTimes[i]).Seconds() > 1 {
-						subscribersMutexes[i].Lock()
-						faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
-						lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
-						var faultLabel string
-						for _, f := range masterConfig.RuleOfObstacleDetection {
-							faultLabel = f(data)
-							if faultLabel != "" {
-								if canCollect() {
-									saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+	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 {
+			create := false // 判断是否创建成功,用于打印日志
+			// 1
+			if topic == masterConfig.TopicOfDiagnostics && len(masterConfig.RuleOfDiagnostics) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *diagnostic_msgs.DiagnosticArray) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfDiagnostics {
+								faultLabel = f(data)
+								if faultLabel != "" {
 									subscribersTimes[i] = time.Now()
-									break
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
 								}
 							}
+							subscribersMutexes[i].Unlock()
 						}
-						subscribersMutexes[i].Unlock()
-					}
-					subscribersTimeMutexes[i].Unlock()
-				},
-			})
-		}
-		if err != nil {
-			c_log.GlobalLogger.Info("创建订阅者", masterConfig.TopicOfObstacleDetection, "发生故障:", err)
-			//TODO 如何回传日志
-			continue
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 2
+			if topic == masterConfig.TopicOfImu && len(masterConfig.RuleOfImu) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *sensor_msgs.Imu) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfImu {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 3
+			if topic == masterConfig.TopicOfLocateInfo && len(masterConfig.RuleOfLocateInfo) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjibot_patrol_msgs.LocateInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfLocateInfo {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 4
+			if topic == masterConfig.TopicOfObstacleDetection && len(masterConfig.RuleOfObstacleDetection) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *std_msgs.UInt8) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfObstacleDetection {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 5
+			if topic == masterConfig.TopicOfOdom && len(masterConfig.RuleOfOdom) > 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 := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfOdom {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 6
+			if topic == masterConfig.TopicOfSysInfo && len(masterConfig.RuleOfSysInfo) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjibot_patrol_msgs.SysInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfSysInfo {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 7
+			if topic == masterConfig.TopicOfRobotPose && len(masterConfig.RuleOfRobotPose) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *geometry_msgs.PoseStamped) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfRobotPose {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 8
+			if topic == masterConfig.TopicOfTaskFeedbackInfo && len(masterConfig.RuleOfTaskFeedbackInfo) > 0 {
+				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
+					Node:  commonConfig.RosNode,
+					Topic: topic,
+					Callback: func(data *pjibot_patrol_msgs.TaskFeedbackInfo) {
+						subscribersTimeMutexes[i].Lock()
+						if time.Since(subscribersTimes[i]).Seconds() > triggerInterval {
+							subscribersMutexes[i].Lock()
+							faultHappenTime := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfTaskFeedbackInfo {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				if err == nil {
+					create = true
+				}
+			}
+			// 9
+			if topic == masterConfig.TopicOfWheelOdom && len(masterConfig.RuleOfWheelOdom) > 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 := commonUtil.GetNowTimeCustom() // 获取当前故障发生时间
+							lastTimeWindow := entity.GetLastTimeWindow()     // 获取最后一个时间窗口
+							var faultLabel string
+							for _, f := range masterConfig.RuleOfWheelOdom {
+								faultLabel = f(data)
+								if faultLabel != "" {
+									subscribersTimes[i] = time.Now()
+									if canCollect() {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,开始采集。", faultLabel)
+										saveTimeWindow(faultLabel, faultHappenTime, lastTimeWindow)
+										break
+									} else {
+										c_log.GlobalLogger.Errorf("触发事件【%v】,但当前周期内采集数量已超限额,不再采集。", faultLabel)
+									}
+								}
+							}
+							subscribersMutexes[i].Unlock()
+						}
+						subscribersTimeMutexes[i].Unlock()
+					},
+				})
+				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 := <-commonService.ChannelKillSubscriber:
 		if signal == 1 {
@@ -148,9 +441,8 @@ func canCollect() bool {
 		return false
 	}
 	if resp.Code != 200 { // 不是200 代表不允许采集
-		c_log.GlobalLogger.Info("采集数量已超过限额,当前周期内不再采集。", resp.Code)
+		c_log.GlobalLogger.Infof("当前周期内采集数量已超过限额。%+v", resp)
 		return false
 	}
-	c_log.GlobalLogger.Info("允许采集。")
 	return true
 }

+ 0 - 0
aarch64/pjibot_patrol/common/config/sh/start-control.sh → aarch64/pjibot_patrol/start-control.sh


+ 0 - 0
aarch64/pjibot_patrol/common/config/sh/start-master.sh → aarch64/pjibot_patrol/start-master.sh


+ 1 - 1
aarch64/pjibot_patrol/common/config/yaml/巡检机器人默认配置文件-cloud-config.yaml → aarch64/pjibot_patrol/巡检机器人默认配置文件-cloud-config.yaml

@@ -63,7 +63,7 @@ hosts:
         - "LD_LIBRARY_PATH=/opt/ros/noetic/lib:/opt/ros/noetic/lib/aarch64-linux-gnu"
         - "ROS_ROOT=/opt/ros/noetic/share/ros"
         - "ROS_DISTRO=noetic"
-    topics:
+    topics: # /robot_pose,/robot/realtime_cost_map_,/tracking/objects,/robot/TaskInfo,/robot/targetposition,/wheel,/wheel_odom,/robot/global_trajectory_,/robot/target_trajectories,/robot/evaluator_trajectories,/robot/final_trajectory,/nav/task_feedback_info,/cmd_vel,/imu,/points_cluster,/nav/task_feedback_info
       - /robot_pose # /location_realtime
       - /robot/realtime_cost_map_ # /cloud_to_map_node
       - /tracking/objects # /tracking/imm_ukf_pda_track

+ 0 - 0
aarch64/pjibot_patrol/common/config/yaml/巡检机器人默认配置文件-local-config.yaml → aarch64/pjibot_patrol/巡检机器人默认配置文件-local-config.yaml


+ 0 - 15
aarch64/pjisuv/README.md

@@ -1,15 +0,0 @@
-# 目录结构
-## common
-- c_cloud.go:阿里云oss配置文件下载解析
-## control 调度程序
-## master 102 程序
-- trigger_init.go:102 初始化加载触发器插件
-## slave 103 程序
-
-
-
-# 添加共享变量
-./master/service/produce_window.go
-
-## 已保存共享变量
-- 

+ 5 - 5
aarch64/pjisuv/common/config/c_cloud.go

@@ -44,7 +44,7 @@ type trigger struct {
 	Topics []string `yaml:"topics"`
 }
 
-type cloudConfig struct {
+type CloudConfigStruct struct {
 	RefreshCloudConfig    bool          `yaml:"refresh-cloud-config"`
 	CompressBag           bool          `yaml:"compress-bag"`
 	CleanBeforeStart      bool          `yaml:"clean-before-start"`
@@ -67,7 +67,7 @@ type cloudConfig struct {
 }
 
 var (
-	CloudConfig      cloudConfig
+	CloudConfig      CloudConfigStruct
 	CloudConfigMutex sync.RWMutex
 )
 
@@ -109,7 +109,7 @@ func InitCloudConfig() {
 	}
 
 	// 4 ------- 解析YAML内容 -------
-	var newCloudConfig cloudConfig
+	var newCloudConfig CloudConfigStruct
 	err = yaml.Unmarshal(content, &newCloudConfig)
 	if err != nil {
 		c_log.GlobalLogger.Error("程序退出。配置文件 ", LocalConfig.CloudConfigLocalPath, " 解析失败:", err)
@@ -171,7 +171,7 @@ func refreshCloudConfig() {
 	}
 
 	// 4 ------- 解析YAML内容 -------
-	var newCloudConfig cloudConfig
+	var newCloudConfig CloudConfigStruct
 	err = yaml.Unmarshal(content, &newCloudConfig)
 	if err != nil {
 		c_log.GlobalLogger.Error("配置文件 ", LocalConfig.CloudConfigLocalPath, " 解析失败:", err)
@@ -200,7 +200,7 @@ func RefreshCloudConfig() {
 }
 
 // CheckConfig 校验 cfg.yaml 文件
-func checkConfig(check cloudConfig) bool {
+func checkConfig(check CloudConfigStruct) bool {
 	if len(check.Hosts) != 2 {
 		c_log.GlobalLogger.Error("cloud-config.yaml中配置的hosts必须为2。")
 		os.Exit(-1)

+ 5 - 5
aarch64/pjisuv/common/config/c_platform.go

@@ -37,8 +37,8 @@ type response struct {
 }
 
 var (
-	PlatformConfig  PlatformConfigStruct
-	SubscribeTopics []string
+	PlatformConfig PlatformConfigStruct
+	RecordTopics   []string
 )
 
 // 初始化数据闭环平台的配置
@@ -54,10 +54,10 @@ func InitPlatformConfig() {
 			continue
 		}
 		if checkPlatformConfig() {
-			SubscribeTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
+			RecordTopics = strings.Split(PlatformConfig.EquipmentTopic, ",")
 			// 去掉首尾空格
-			for i, topic := range SubscribeTopics {
-				SubscribeTopics[i] = strings.TrimSpace(topic)
+			for i, topic := range RecordTopics {
+				RecordTopics[i] = strings.TrimSpace(topic)
 			}
 			break
 		}

+ 10 - 5
aarch64/pjisuv/common/service/rosbag_record.go

@@ -10,15 +10,15 @@ import (
 
 // BagRecord 打包rosbag
 func BagRecord(nodeName string) {
-	c_log.GlobalLogger.Info("rosbag record goroutine - 启动")
+	c_log.GlobalLogger.Info("rosbag record goroutine - 启动")
 	for {
-		c_log.GlobalLogger.Info("校验必需的 rosnode 是否全部启动。")
+		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("rosnode启动完成,正在启动record命令。")
+		c_log.GlobalLogger.Info("ROS NODE 启动完成,正在启动record命令。")
 
 		var command []string
 		command = append(command, "record")
@@ -26,8 +26,13 @@ func BagRecord(nodeName string) {
 		command = append(command, "--duration=1")
 		for _, host := range config.CloudConfig.Hosts {
 			if host.Name == nodeName {
-				for _, topic := range host.Topics {
-					command = append(command, topic)
+				// 配置文件中的是node1和node2各自的所有topic,platformConfig中配置用户想缓存的topic,取交集
+				for _, topic1 := range config.RecordTopics {
+					for _, topic2 := range host.Topics {
+						if topic1 == topic2 {
+							command = append(command, topic1)
+						}
+					}
 				}
 			}
 		}

+ 32 - 5
aarch64/pjisuv/common/service/rosbag_upload.go

@@ -46,13 +46,40 @@ outLoop:
 		entity.RemoveHeadOfTimeWindowConsumerQueue()
 		c_log.GlobalLogger.Infof("开始处理窗口,【Lable】=%v,【FaultTime】=%v,【Length】=%v", currentTimeWindow.Labels, currentTimeWindow.FaultTime, currentTimeWindow.Length)
 
-		// 2 获取目录
+		// 2 获取目录和bags
 		dir := domain.GetCopyDir(commonConfig.CloudConfig.BagCopyDir, currentTimeWindow.FaultTime)
 		//bags, _ := commonUtil.ListAbsolutePathWithSuffixAndSort(dir, ".bag")
-		//bagNumber := len(bags)
-		//if bagNumber > currentTimeWindow.Length {
-		//	bagNumber = currentTimeWindow.Length
-		//	bags = bags[0:currentTimeWindow.Length]
+		// 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  {

+ 1 - 1
aarch64/pjisuv/master/config/trigger_init.go

@@ -83,7 +83,7 @@ func InitTriggerConfig() {
 		}
 
 		if pjisuv_ticker.TickerTopic == topic2 { // 定时任务触发器
-			if f, ok1 := rule.(func(shareVars *sync.Map) string); ok1 {
+			if f, ok1 := rule.(func(shareVars *sync.Map)); ok1 {
 				RuleOfCicvTicker = append(RuleOfCicvTicker, f)
 				goto JudgeDone
 			}

+ 90 - 40
aarch64/pjisuv/master/config/trigger_var.go

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

+ 139 - 15
aarch64/pjisuv/master/service/produce_window.go

@@ -48,6 +48,7 @@ var (
 	numCountDataReadOfDataRead int
 	egoSteeringRealOfDataRead  []float64
 	egoThrottleRealOfDataRead  []float64
+	GearPosSlice               = []int16{}
 	// --------------------------------------------------
 	shareVars           = new(sync.Map)
 	saveTimeWindowMutex sync.Mutex // 保存时间窗口需要锁,防止数据竟态
@@ -60,11 +61,11 @@ func ProduceWindow() {
 	c_log.GlobalLogger.Info("订阅者 goroutine,启动。")
 
 	var err error
-	subscribers := make([]*goroslib.Subscriber, len(commonConfig.SubscribeTopics))
-	subscribersTimes := make([]time.Time, len(commonConfig.SubscribeTopics))
-	subscribersTimeMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	subscribersMutexes := make([]sync.Mutex, len(commonConfig.SubscribeTopics))
-	for i, topic := range commonConfig.SubscribeTopics {
+	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 {
@@ -87,10 +88,7 @@ func ProduceWindow() {
 					}
 				}()
 			}
-
-			// 其他常规监听器
-			c_log.GlobalLogger.Info("创建订阅者订阅话题:" + topic)
-
+			create := false // 判断是否创建成功,用于打印日志
 			// 1
 			if topic == masterConfig.TopicOfAmrPose && (len(masterConfig.RuleOfAmrPose1) > 0 || len(masterConfig.RuleOfAmrPose3) > 0) {
 				subscribers[i], err = goroslib.NewSubscriber(goroslib.SubscriberConf{
@@ -129,6 +127,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 2
@@ -171,6 +172,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 3
@@ -214,6 +218,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 4
@@ -257,6 +264,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 5
@@ -300,6 +310,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 6
@@ -343,6 +356,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 7
@@ -386,6 +402,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 8
@@ -429,6 +448,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 9
@@ -471,6 +493,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 10
@@ -514,6 +539,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 11 有共享变量的订阅者必须被创建
@@ -561,6 +589,9 @@ func ProduceWindow() {
 						shareVars.Store("DecisionType", data.Trajectoryinfo.DecisionType)
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 12 有共享变量的订阅者必须被创建
@@ -601,8 +632,9 @@ func ProduceWindow() {
 						}
 						subscribersTimeMutexes[i].Unlock()
 						// 更新共享变量
-						AbsAccel := math.Sqrt(math.Pow(data.AccelX, 2) + math.Pow(data.AccelY, 2))
-						AccelXSlice = append(AccelXSlice, AbsAccel)
+						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)
@@ -623,6 +655,9 @@ func ProduceWindow() {
 						}
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 13
@@ -667,6 +702,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 14
@@ -710,6 +748,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 15
@@ -754,6 +795,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 16
@@ -798,6 +842,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 17
@@ -841,6 +888,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 18
@@ -884,6 +934,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 19
@@ -927,6 +980,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 20
@@ -971,6 +1027,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 21
@@ -1014,6 +1073,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 22 有共享变量的订阅者必须被创建
@@ -1066,6 +1128,9 @@ func ProduceWindow() {
 						shareVars.Store("EgoThrottleCmdOfPjControlPub", egoThrottleCmdOfPjControlPub)
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 23
@@ -1109,6 +1174,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 24
@@ -1152,6 +1220,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 25
@@ -1195,6 +1266,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 26
@@ -1238,6 +1312,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 27
@@ -1281,6 +1358,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 28
@@ -1324,6 +1404,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 29
@@ -1367,6 +1450,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 30 有共享变量的订阅者必须被创建
@@ -1410,17 +1496,19 @@ func ProduceWindow() {
 
 						// 更新共享变量
 						for _, obj := range data.Objs {
-							if obj.X <= 5 || math.Abs(float64(obj.Y)) >= 10 {
+							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] = [][]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)
+
 							objTypeDicOfTpperception[obj.Id] = obj.Type
 							objSpeedDicOfTpperception[obj.Id] = math.Pow(math.Pow(float64(obj.Vxabs), 2)+math.Pow(float64(obj.Vyabs), 2), 0.5)
 						}
@@ -1429,6 +1517,9 @@ func ProduceWindow() {
 						shareVars.Store("ObjSpeedDicOfTpperception", objSpeedDicOfTpperception)
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 31
@@ -1472,6 +1563,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 32
@@ -1515,6 +1609,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 33
@@ -1558,6 +1655,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 34
@@ -1601,6 +1701,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 35
@@ -1645,6 +1748,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 36 有共享变量的订阅者必须被创建
@@ -1691,12 +1797,17 @@ func ProduceWindow() {
 							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 &&
@@ -1739,6 +1850,9 @@ func ProduceWindow() {
 						subscribersTimeMutexes[i].Unlock()
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 
 			// 39 有共享变量的订阅者必须被创建
@@ -1782,6 +1896,9 @@ func ProduceWindow() {
 						shareVars.Store("AutomodeOfPjVehicleFdbPub", data.Automode)
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 			// 40 有共享变量的订阅者必须被创建
 			if topic == masterConfig.TopicOfEndPointMessage {
@@ -1826,17 +1943,24 @@ func ProduceWindow() {
 						shareVars.Store("EndPointY", data.Y)
 					},
 				})
+				if err == nil {
+					create = true
+				}
 			}
 			if err != nil {
-				c_log.GlobalLogger.Info("创建订阅者报错,可能由于节点未启动,再次尝试:", err)
+				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 {

+ 0 - 0
aarch64/pjisuv/common/config/sh/remove.sh → aarch64/pjisuv/remove.sh


+ 0 - 0
aarch64/pjisuv/common/config/sh/start-soc1.sh → aarch64/pjisuv/start-soc1.sh


+ 0 - 0
aarch64/pjisuv/common/config/sh/start-soc2.sh → aarch64/pjisuv/start-soc2.sh


+ 3 - 5
aarch64/pjisuv/common/config/yaml/通用-cloud-config.yaml → aarch64/pjisuv/多功能车-cloud-config.yaml

@@ -15,10 +15,6 @@ config-refresh-interval: 60
 disk:
   name: /dev/vdb # 磁盘名称
   used: 900000000000 # 磁盘占用阈值,单位bytes
-#bag-data-dir: /userdata/cicv-data-closedloop/data/
-#bag-copy-dir: /userdata/cicv-data-closedloop/copy/
-#time-to-label-json-path: /userdata/cicv-data-closedloop/timeToLabel.json
-#triggers-dir: /userdata/cicv-data-closedloop/triggers/
 bag-data-dir: /mnt/media/sda1/cicv-data-closedloop/data/
 bag-copy-dir: /mnt/media/sda1/cicv-data-closedloop/copy/
 time-to-label-json-path: /mnt/media/sda1/cicv-data-closedloop/timeToLabel.json
@@ -34,6 +30,8 @@ ros:
     - /lidar_pretreatment
     - /sensorfusion
     - /control
+
+# /camera_image,/cicv_amr_trajectory,/pj_control_pub,/data_read,/tftrafficlight,/cicv_amr_trajectory,/trajectory_display,/reference_display,/reference_trajectory,/tprouteplan,/map_polygon,/vehicle_info,/target_line,/parking_line,/station_status,/pj_control_pub,/pj_control_debug_pub,/tar_traj_pub,/park_task,/parking_line,/map_display,/v2x_planner,/end_point_message,/heartbeat_info,/fault_info,/planning_fault_info,/nodefault_info,/points_concat,/tpperception,/cicv_location,/tpperception,/pull_over,/pj_vehicle_fdb_pub,/pre_line,/target_line,/amr_pose,/destination_pose,/lidar_roi,/obstacle_display,/target_point_pub,/fusion/traffic_light,/roi/polygon,/tpperception/vis
 hosts:
   - name: node1
     ip: 192.168.1.102
@@ -172,4 +170,4 @@ triggers:
       - /tpperception
       - /points_concat
       - /cicv_location
-      - /camera_image
+      - /camera_image

+ 0 - 0
aarch64/pjisuv/common/config/yaml/通用-soc1-local-config.yaml → aarch64/pjisuv/多功能车-soc1-local-config.yaml


+ 0 - 0
aarch64/pjisuv/common/config/yaml/通用-soc2-local-config.yaml → aarch64/pjisuv/多功能车-soc2-local-config.yaml


+ 29 - 0
common/service/cache_monitor_queue.go

@@ -0,0 +1,29 @@
+package service
+
+import (
+	"cicv-data-closedloop/common/util"
+	"sync"
+)
+
+var (
+	MonitorQueue = make([]MonitorInfo, 0, QueueLength)
+	QueueLength  = 120
+	mu           sync.Mutex // 用于保护MonitorQueue的互斥锁
+)
+
+type MonitorInfo struct {
+	Time string
+}
+
+// CacheMonitorQueue 向MonitorQueue中添加一个新的MonitorInfo实例,如果队列已满,则替换最旧的元素
+func CacheMonitorQueue() {
+	mu.Lock()
+	defer mu.Unlock()
+	// 创建新的MonitorInfo实例,Time字段为当前时间的字符串表示
+	// 如果队列长度已经达到上限,移除最旧的元素
+	if len(MonitorQueue) >= QueueLength {
+		MonitorQueue = MonitorQueue[1:] // 移除第一个元素
+	}
+	// 将新的MonitorInfo实例添加到队列的末尾
+	MonitorQueue = append(MonitorQueue, MonitorInfo{Time: util.GetNowTimeCustom()})
+}

+ 0 - 25
kinglong_param/kinglong_param.go

@@ -1,25 +0,0 @@
-package kinglong_param
-
-type KinglongParam struct {
-	// /cicv_location
-	VelocityXOfCicvLocation        float64
-	VelocityYOfCicvLocation        float64
-	YawOfCicvLocation              float64
-	AngularVelocityZOfCicvLocation float64
-
-	// /tpperception
-	ObjDicOfTpperception      map[uint32][]float32
-	ObjTypeDicOfTpperception  map[uint32]uint8
-	ObjSpeedDicOfTpperception map[uint32]float64
-
-	// /pji_control_pub
-	NumCountJinlongControlCommandOfPjControlPub int
-	EgoSteeringCmdOfJinlongControlPub           []float64
-	EgoThrottleCmdOfJinlongControlPub           []float64
-	EgoBrakeCmdOfJinlongControlPub              []float64
-	// /data_read
-	NumCountDataReadOfDataRead int
-	EgoSteeringRealOfDataRead  []float64
-	EgoThrottleRealOfDataRead  []float64
-	EgoBrakeRealOfDataRead     []float64
-}

+ 5 - 0
pjibot_delivery_msgs/common_msgs.go

@@ -11,11 +11,16 @@ type SysInfo struct {
 	MemOccupied    float32 `rosname:"mem_occupied"`
 	CurMileage     float64 `rosname:"cur_mileage"`
 	HistoryMileage float64 `rosname:"history_mileage"`
+	DiskOccupied   float64 `rosname:"disk_occupied"`
 }
 
 type LocateInfo struct {
 	msg.Package  `ros:"common_msgs"`
 	Pose         geometry_msgs.PoseStamped `rosname:"pose"`
+	Lat          float64                   `rosname:"lat"`
+	Lon          float64                   `rosname:"lon"`
+	StarNum      int8                      `rosname:"star_num"`
+	RtkStatus    int8                      `rosname:"rtk_status"`
 	LocateStatus int8                      `rosname:"locate_status"`
 	ErrorCode    int64                     `rosname:"error_code"`
 	Message      string                    `rosname:"message"`

+ 5 - 0
pjibot_patrol_msgs/common_msgs.go

@@ -11,11 +11,16 @@ type SysInfo struct {
 	MemOccupied    float32 `rosname:"mem_occupied"`
 	CurMileage     float64 `rosname:"cur_mileage"`
 	HistoryMileage float64 `rosname:"history_mileage"`
+	DiskOccupied   float64 `rosname:"disk_occupied"`
 }
 
 type LocateInfo struct {
 	msg.Package  `ros:"common_msgs"`
 	Pose         geometry_msgs.PoseStamped `rosname:"pose"`
+	Lat          float64                   `rosname:"lat"`
+	Lon          float64                   `rosname:"lon"`
+	StarNum      int8                      `rosname:"star_num"`
+	RtkStatus    int8                      `rosname:"rtk_status"`
 	LocateStatus int8                      `rosname:"locate_status"`
 	ErrorCode    int64                     `rosname:"error_code"`
 	Message      string                    `rosname:"message"`

+ 2 - 2
trigger/pjibot_delivery/imu/unstabledriving/main/main.go → trigger/pjibot_delivery/imu/UnstableDriving/main/UnstableDriving.go

@@ -10,12 +10,12 @@ func Topic() string {
 }
 
 func Label() string {
-	return "unstabledriving"
+	return "UnstableDriving"
 }
 
 func Rule(data *sensor_msgs.Imu) string {
 	if math.Abs(data.LinearAcceleration.X) >= 0.7 && math.Abs(data.LinearAcceleration.Y) >= 0.7 {
-		return "unstabledriving"
+		return Label()
 	}
 	return ""
 }

+ 2 - 2
trigger/pjibot_delivery/locate_info/locationfailed/main/main.go → trigger/pjibot_delivery/locate_info/LocationFailed/main/LocationFailed.go

@@ -10,12 +10,12 @@ func Topic() string {
 }
 
 func Label() string {
-	return "locationfailed"
+	return "LocationFailed"
 }
 
 func Rule(data *pjibot_delivery_msgs.LocateInfo) string {
 	if data.LocateStatus == 0 {
-		return "locationfailed"
+		return Label()
 	}
 	return ""
 }

+ 11 - 7
trigger/pjibot_delivery/robot_pose/EnterTjunction/main/EnterTjunction.go

@@ -23,12 +23,13 @@ type Point struct {
 var (
 
 	//定义园区部门T字路口UTM坐标
-	point3 = Point{35.5711, 150.49}
-	point4 = Point{108.108, 197.537}
-	point5 = Point{76.0262, 78.9898}
-	point6 = Point{149.308, 118.211}
-	point7 = Point{105.141, 21.5495}
-	point8 = Point{180.037, 64.8318}
+	mindistance float64 = 999
+	point3              = Point{35.5711 + 88.96626338170609, 150.49 + 40.553671177476645}
+	point4              = Point{108.108 + 88.96626338170609, 197.537 + 40.553671177476645}
+	point5              = Point{76.0262 + 88.96626338170609, 78.9898 + 40.553671177476645}
+	point6              = Point{149.308 + 88.96626338170609, 118.211 + 40.553671177476645}
+	point7              = Point{105.141 + 88.96626338170609, 21.5495 + 40.553671177476645}
+	point8              = Point{180.037 + 88.96626338170609, 64.8318 + 40.553671177476645}
 
 	pointlist = []Point{point3, point4, point5, point6, point7, point8}
 )
@@ -39,6 +40,7 @@ func Rule(data *geometry_msgs.PoseStamped) string {
 			fmt.Println("Recovered from panic:", r)
 		}
 	}()
+	//fmt.Println("判断是否进入T字路口")
 	enterflag := IfEnter(pointlist, 15, data.Pose.Position.X, data.Pose.Position.Y)
 	if enterflag {
 		return "EnterTjunction"
@@ -51,16 +53,18 @@ func IfEnter(pointlist []Point, radius float64, x, y float64) bool {
 	point1 := Point{X: x, Y: y}
 	for _, point := range pointlist {
 		d := distance(point1, point)
+		mindistance = min(d, mindistance)
 		if d <= radius {
 			return true
 		}
 	}
+	//fmt.Printf("mindistance=%f", mindistance)
+	mindistance = 999.0
 	return false
 }
 
 // 计算两点之间的距离(米)
 func distance(point1, point2 Point) float64 {
 	d := math.Sqrt((point2.X-point1.X)*(point2.X-point1.X) + (point2.Y-point1.Y)*(point2.Y-point1.Y))
-
 	return d
 }

+ 12 - 11
trigger/pjibot_delivery/robot_pose/PassManholeCover/main/PassManholeCover.go

@@ -10,7 +10,7 @@ func Topic() string {
 	return "/robot_pose"
 }
 
-// Label todo 禁止存在下划线_
+// 禁止存在下划线_
 func Label() string {
 	return "PassManholeCover"
 }
@@ -23,11 +23,11 @@ type Point struct {
 var (
 
 	//定义园区部分井盖UTM坐标
-	point3 = Point{-29.5885, 108.087}
-	point4 = Point{209.923, 210.394}
-	point5 = Point{16.441, -37.0252}
-	point6 = Point{-53.9421, -74.9124}
-	point7 = Point{232.502, 172.084}
+	point3 = Point{-29.5885 + 88.96626338170609, 108.087 + 40.553671177476645}
+	point4 = Point{209.923 + 88.96626338170609, 210.394 + 40.553671177476645}
+	point5 = Point{16.441 + 88.96626338170609, -37.0252 + 40.553671177476645}
+	point6 = Point{-53.9421 + 88.96626338170609, -74.9124 + 40.553671177476645}
+	point7 = Point{232.502 + 88.96626338170609, 172.084 + 40.553671177476645}
 
 	pointlist = []Point{point3, point4, point5, point6, point7}
 )
@@ -38,17 +38,18 @@ func Rule(data *geometry_msgs.PoseStamped) string {
 			fmt.Println("Recovered from panic:", r)
 		}
 	}()
-	enterflag := IfEnter(pointlist, 1.5, data.Pose.Position.X, data.Pose.Position.Y)
-	if enterflag {
-		return "PassManholeCover"
+	//fmt.Println("判断是否覆盖井盖")
+	enterFlag := IfEnter(pointlist, 1.5, data.Pose.Position.X, data.Pose.Position.Y)
+	if enterFlag {
+		return Label()
 	}
 
 	return ""
 }
-func IfEnter(pointlist []Point, radius float64, x, y float64) bool {
+func IfEnter(pointList []Point, radius float64, x, y float64) bool {
 	// 判断是否进入点列表中的区域
 	point1 := Point{X: x, Y: y}
-	for _, point := range pointlist {
+	for _, point := range pointList {
 		d := distance(point1, point)
 		if d <= radius {
 			return true

+ 2 - 2
trigger/pjibot_delivery/sys_info/cpuoveroccupied/main/main.go → trigger/pjibot_delivery/sys_info/CpuOverOccupied/main/CpuOverOccupied.go

@@ -9,12 +9,12 @@ func Topic() string {
 }
 
 func Label() string {
-	return "cpuoveroccupied"
+	return "CpuOverOccupied"
 }
 
 func Rule(data *pjibot_delivery_msgs.SysInfo) string {
 	if data.CpuOccupied > 99 {
-		return "cpuoveroccupied"
+		return Label()
 	}
 	return ""
 }

+ 2 - 2
trigger/pjibot_delivery/sys_info/memoveroccupied/main/main.go → trigger/pjibot_delivery/sys_info/MemOverOccupied/main/MemOverOccupied.go

@@ -9,12 +9,12 @@ func Topic() string {
 }
 
 func Label() string {
-	return "memoveroccupied"
+	return "MemOverOccupied"
 }
 
 func Rule(data *pjibot_delivery_msgs.SysInfo) string {
 	if data.MemOccupied > 99 {
-		return "memoveroccupied"
+		return Label()
 	}
 	return ""
 }

+ 1 - 1
trigger/pjibot_delivery/task_feedback_info/TaskFailed/main/TaskFailed.go

@@ -16,7 +16,7 @@ func Label() string {
 func Rule(data *pjibot_delivery_msgs.TaskFeedbackInfo) string {
 
 	if data.TaskErrorCode != 0 {
-		return "TaskFailed"
+		return Label()
 	}
 	return ""
 }

+ 49 - 0
trigger/pjibot_delivery/wheel_odom/AuLongStop/main/AuLongStop.go

@@ -0,0 +1,49 @@
+package main
+
+import (
+	"fmt"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/nav_msgs"
+	"math"
+	"time"
+)
+
+func Topic() string {
+	return "/wheel_odom"
+}
+
+func Label() string {
+	return "AuLongStop"
+}
+
+var (
+	StartTime int64
+	IsStopped bool
+)
+
+func Rule(data *nav_msgs.Odometry) string {
+	velocityX := data.Twist.Twist.Linear.X
+	velocityY := data.Twist.Twist.Linear.Y
+	absV := math.Pow(math.Pow(velocityX, 2)+math.Pow(velocityY, 2), 0.5)
+	if absV < 0.3 {
+		// 如果之前没有记录开始时间,记录当前时间
+		if StartTime == 0 {
+			StartTime = time.Now().Unix()
+		}
+
+		// 判断是否持续超过一分钟
+		if time.Now().Unix()-StartTime > 10 {
+			if !IsStopped {
+				event_label := "AuLongStop"
+				fmt.Println(event_label)
+				IsStopped = true
+				return Label()
+			}
+		}
+	} else {
+		// 如果速度大于 0.1,重置开始时间和停止标志
+		StartTime = 0
+		//is_stopped = false
+	}
+	return ""
+
+}

+ 3 - 1
trigger/pjisuv/cicv_location/AbnormalParking/main/AbnormalParking.go

@@ -76,6 +76,7 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	if count1%10 == 0 {
 		OutsideWorkshopFlag, _ := shareVars.Load("OutsideWorkshopFlag")
 		OutsideWorkshopFlag = OutsideWorkshopFlag.(bool)
+
 		Automode, _ := shareVars.Load("AutomodeOfPjVehicleFdbPub")
 		Longitude, _ := shareVars.Load("EndPointX")
 		Latitude, _ := shareVars.Load("EndPointY")
@@ -86,7 +87,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 		IsTrafficLight = IfEnter(pointlist1, 30.0, data.Latitude, data.Longitude)
 		IsEndPoint = IfEnter(pointlist2, 5.0, data.Latitude, data.Longitude)
 		if Automode == 1 && !IsTrafficLight && !IsEndPoint && OutsideWorkshopFlag == true {
-			if data.VelocityX < 0.5 {
+			AbsSpeed, _ := shareVars.Load("AbsSpeed")
+			if AbsSpeed.(float64) < 0.5 {
 				// 如果之前没有记录开始时间,记录当前时间
 				if StartTime == 0 {
 					StartTime = time.Now().Unix()

+ 10 - 3
trigger/pjisuv/cicv_location/AbnormalStopOnCurve/main/AbnormalStopOnCurve.go

@@ -72,8 +72,14 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	}()
 	if count1%10 == 0 {
 		Automode, _ := shareVars.Load("AutomodeOfPjVehicleFdbPub")
-		Longitude, _ := shareVars.Load("EndPointX")
-		Latitude, _ := shareVars.Load("EndPointY")
+		Longitude, ok1 := shareVars.Load("EndPointX")
+		if !ok1 {
+			Longitude = 0.0
+		}
+		Latitude, ok2 := shareVars.Load("EndPointY")
+		if !ok2 {
+			Latitude = 0.0
+		}
 		Automode = Automode.(int16)
 		EndPoint.Longitude = Longitude.(float64)
 		EndPoint.Latitude = Latitude.(float64)
@@ -81,7 +87,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 		IsCurve = IfEnter(pointlist1, 30.0, data.Latitude, data.Longitude)
 		IsEndPoint = IfEnter(pointlist2, 5.0, data.Latitude, data.Longitude)
 		if Automode == 1 && IsCurve && !IsEndPoint {
-			if data.VelocityX < 0.5 {
+			AbsSpeed, _ := shareVars.Load("AbsSpeed")
+			if AbsSpeed.(float64) < 0.5 {
 				// 如果之前没有记录开始时间,记录当前时间
 				if StartTime == 0 {
 					StartTime = time.Now().Unix()

+ 10 - 3
trigger/pjisuv/cicv_location/AbnormalStopOnJunction/main/AbnormalStopOnJunction.go

@@ -78,8 +78,14 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	}()
 	if count1%10 == 0 {
 		Automode, _ := shareVars.Load("AutomodeOfPjVehicleFdbPub")
-		Longitude, _ := shareVars.Load("EndPointX")
-		Latitude, _ := shareVars.Load("EndPointY")
+		Longitude, ok1 := shareVars.Load("EndPointX")
+		if !ok1 {
+			Longitude = 0.0
+		}
+		Latitude, ok2 := shareVars.Load("EndPointY")
+		if !ok2 {
+			Latitude = 0.0
+		}
 		Automode = Automode.(int16)
 		EndPoint.Longitude = Longitude.(float64)
 		EndPoint.Latitude = Latitude.(float64)
@@ -87,7 +93,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 		IsJunction = IfEnter(PointJunctionList, 25.0, data.Latitude, data.Longitude)
 		IsEndPoint = IfEnter(pointlist2, 5.0, data.Latitude, data.Longitude)
 		if Automode == 1 && IsJunction && !IsEndPoint {
-			if data.VelocityX < 0.5 {
+			AbsSpeed, _ := shareVars.Load("AbsSpeed")
+			if AbsSpeed.(float64) < 0.5 {
 				// 如果之前没有记录开始时间,记录当前时间
 				if StartTime == 0 {
 					StartTime = time.Now().Unix()

+ 35 - 0
trigger/pjisuv/cicv_location/BrakeWithHighSpeed/main/BrakeWithHighSpeed.go

@@ -0,0 +1,35 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_msgs"
+	"fmt"
+	"sync"
+)
+
+var (
+	threshold float64 = 5.4
+)
+
+func Topic() string {
+	return "/cicv_location"
+}
+
+// 禁止存在下划线_
+func Label() string {
+	return "BrakeWithHighSpeed"
+}
+
+func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	OutsideWorkshopFlag, _ := shareVars.Load("OutsideWorkshopFlag")
+	OutsideWorkshopFlag = OutsideWorkshopFlag.(bool)
+	AbsSpeed, _ := shareVars.Load("AbsSpeed")
+	if data.AccelX*9.8 < -12.0 && OutsideWorkshopFlag == true && AbsSpeed.(float64) > threshold {
+		return Label()
+	}
+	return ""
+}

+ 128 - 0
trigger/pjisuv/cicv_location/CannotBypassObstacles/main/CannotBypassObstacles.go

@@ -0,0 +1,128 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_msgs"
+	"fmt"
+	"math"
+	"sync"
+	"time"
+)
+
+type Point struct {
+	Latitude  float64
+	Longitude float64
+}
+
+var (
+	StartTime      int64
+	IsStopped      bool
+	IsEndPoint     bool
+	IsTrafficLight bool
+	count1         int64
+	//定义园区4个信号灯的坐标
+	point2     = Point{39.72975930689718, 116.48861102824081}
+	point3     = Point{39.7288805296616, 116.48812315228867}
+	point4     = Point{39.73061430369551, 116.49225103553502}
+	point5     = Point{39.73077491578002, 116.49060085035634}
+	EndPoint   = Point{0, 0}
+	pointlist1 = []Point{point2, point3, point4, point5}
+)
+
+func Topic() string {
+	return "/cicv_location"
+}
+
+// 禁止存在下划线_
+func Label() string {
+	return "CannotBypassObstacles"
+}
+
+func IfEnter(pointlist []Point, radius float64, lat, lon float64) bool {
+	// 判断是否进入点列表中的区域
+	point1 := Point{Latitude: lat, Longitude: lon}
+	for _, point := range pointlist {
+		d := distance(point1, point)
+		if d <= radius {
+			return true
+		}
+	}
+	return false
+}
+
+// 计算两点之间的距离(米)
+func distance(point1, point2 Point) 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
+}
+func IfObstaclesNearby(shareVars *sync.Map) bool {
+	ObjDicOfTpperception, ok1 := shareVars.Load("objDicOfTpperception")
+	ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
+	if ok1 {
+		for _, obj := range ObjDic {
+			if obj[0][len(obj[0])-1] <= 13 && obj[0][len(obj[0])-1] >= 3 && (math.Abs(float64(obj[1][len(obj[1])-1]))) <= 6 {
+				return true
+
+			}
+		}
+	}
+	return false
+}
+func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	if count1%10 == 0 {
+		OutsideWorkshopFlag, _ := shareVars.Load("OutsideWorkshopFlag")
+		OutsideWorkshopFlag = OutsideWorkshopFlag.(bool)
+		Automode, _ := shareVars.Load("AutomodeOfPjVehicleFdbPub")
+		Longitude, _ := shareVars.Load("EndPointX")
+		Latitude, _ := shareVars.Load("EndPointY")
+
+		Automode = Automode.(int16)
+		EndPoint.Longitude = Longitude.(float64)
+		EndPoint.Latitude = Latitude.(float64)
+		pointlist2 := []Point{EndPoint}
+		IsTrafficLight = IfEnter(pointlist1, 30.0, data.Latitude, data.Longitude)
+		IsEndPoint = IfEnter(pointlist2, 5.0, data.Latitude, data.Longitude)
+		if Automode == 1 && !IsTrafficLight && !IsEndPoint && OutsideWorkshopFlag == true {
+			AbsSpeed, _ := shareVars.Load("AbsSpeed")
+			flag := IfObstaclesNearby(shareVars)
+			if AbsSpeed.(float64) < 0.5 && flag {
+				// 如果之前没有记录开始时间,记录当前时间
+				if StartTime == 0 {
+					StartTime = time.Now().Unix()
+				}
+				// 判断是否持续超过 50s
+				if time.Now().Unix()-StartTime > 5 {
+					if !IsStopped {
+						IsStopped = true
+						return Label()
+					}
+				}
+			} else {
+				// 如果速度大于 0.1,重置开始时间和停止标志
+				StartTime = 0
+				IsStopped = false
+			}
+		} else {
+			StartTime = 0
+			IsStopped = false
+		}
+	}
+	count1++
+	return ""
+}

+ 3 - 3
trigger/pjisuv/cicv_location/CurveOverspeed/main/CurveOverspeed.go

@@ -13,7 +13,7 @@ type Point struct {
 }
 
 var (
-	threshold  float64 = 10
+	threshold  float64 = 5
 	IsCurve    bool
 	count1     int64
 	pointcurve = Point{39.73004426154644, 116.49248639463602}
@@ -71,8 +71,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 		IsCurve = IfEnter(pointlist1, 30.0, data.Latitude, data.Longitude)
 
 		if Automode == 1 && IsCurve {
-			absspeed := math.Sqrt(math.Pow(data.VelocityX, 2) + math.Pow(data.VelocityY, 2))
-			if absspeed >= threshold {
+			absspeed, ok := shareVars.Load("AbsSpeed")
+			if ok && absspeed.(float64) >= threshold {
 				eventLabel := "CurveOverspeed"
 				fmt.Println(eventLabel)
 				count1 = 1

+ 64 - 0
trigger/pjisuv/cicv_location/EgoReversing/main/EgoReversing.go

@@ -0,0 +1,64 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_msgs"
+	"fmt"
+	"math"
+	"sync"
+)
+
+var (
+	count1 int = 0
+)
+
+func Topic() string {
+	return "/cicv_location"
+}
+
+// 禁止存在下划线_
+func Label() string {
+	return "EgoReversing"
+}
+func calculateDirectionAngle(speedX, speedY float64) float64 {
+	// 使用反正切函数计算方向角
+	angle := math.Atan2(speedY, speedX)
+
+	// 将角度转换为度数
+	angleDegree := angle * (180 / math.Pi)
+
+	// 将角度限定在 0~360 范围内
+	if angleDegree < 0 {
+		angleDegree += 360
+	} else if angleDegree > 360 {
+		angleDegree -= 360
+	}
+
+	return angleDegree
+}
+
+// 主进程的逻辑是先判断触发再缓存全局变量
+func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	if count1%200 == 0 {
+		OutsideWorkshopFlag, ok3 := shareVars.Load("OutsideWorkshopFlag")
+		if ok3 {
+			directionAngle := calculateDirectionAngle(data.VelocityX, data.VelocityY)
+			AbsSpeed, _ := shareVars.Load("AbsSpeed")
+			diffAngle := math.Abs(float64(directionAngle - data.Yaw))
+			//fmt.Println(diffAngle)
+			if AbsSpeed.(float64) >= 1 && diffAngle >= 160 && diffAngle <= 200 && OutsideWorkshopFlag.(bool) {
+
+				eventLabel := "EgoReversing"
+				fmt.Println(eventLabel)
+				count1 = 1
+				return Label()
+			}
+		}
+	}
+	count1++
+	return ""
+}

+ 2 - 2
trigger/pjisuv/cicv_location/FindTrafficLightP1/main/FindTrafficLightP1.go

@@ -42,8 +42,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	if count1%10 == 0 {
 
 		enterflag := IfEnter(point, 25.0, data.Latitude, data.Longitude)
-		VelocityXOfCicvLocation, _ := shareVars.Load("VelocityXOfCicvLocation")
-		if enterflag && VelocityXOfCicvLocation.(float64) > 1 {
+		AbsSpeed, _ := shareVars.Load("AbsSpeed")
+		if enterflag && AbsSpeed.(float64) > 1 {
 			//eventLabel := "FindTrafficLight"
 			//fmt.Println(eventLabel)
 			return "FindTrafficLightP1"

+ 2 - 2
trigger/pjisuv/cicv_location/FindTrafficLightP2/main/FindTrafficLightP2.go

@@ -42,8 +42,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	if count1%10 == 0 {
 
 		enterflag := IfEnter(point, 25.0, data.Latitude, data.Longitude)
-		VelocityXOfCicvLocation, _ := shareVars.Load("VelocityXOfCicvLocation")
-		if enterflag && VelocityXOfCicvLocation.(float64) > 1 {
+		AbsSpeed, _ := shareVars.Load("AbsSpeed")
+		if enterflag && AbsSpeed.(float64) > 1 {
 			//eventLabel := "FindTrafficLight"
 			//fmt.Println(eventLabel)
 			return "FindTrafficLightP1"

+ 2 - 2
trigger/pjisuv/cicv_location/FindTrafficLightP3/main/FindTrafficLightP3.go

@@ -42,8 +42,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	if count1%10 == 0 {
 
 		enterflag := IfEnter(point, 25.0, data.Latitude, data.Longitude)
-		VelocityXOfCicvLocation, _ := shareVars.Load("VelocityXOfCicvLocation")
-		if enterflag && VelocityXOfCicvLocation.(float64) > 1 {
+		AbsSpeed, _ := shareVars.Load("AbsSpeed")
+		if enterflag && AbsSpeed.(float64) > 1 {
 			//eventLabel := "FindTrafficLight"
 			//fmt.Println(eventLabel)
 			return "FindTrafficLightP1"

+ 2 - 2
trigger/pjisuv/cicv_location/FindTrafficLightP4/main/FindTrafficLightP4.go

@@ -42,8 +42,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	if count1%10 == 0 {
 
 		enterflag := IfEnter(point, 25.0, data.Latitude, data.Longitude)
-		VelocityXOfCicvLocation, _ := shareVars.Load("VelocityXOfCicvLocation")
-		if enterflag && VelocityXOfCicvLocation.(float64) > 1 {
+		AbsSpeed, _ := shareVars.Load("AbsSpeed")
+		if enterflag && AbsSpeed.(float64) > 1 {
 			//eventLabel := "FindTrafficLight"
 			//fmt.Println(eventLabel)
 			return "FindTrafficLightP1"

+ 3 - 3
trigger/pjisuv/cicv_location/JunctionOverspeed/main/JunctionOverspeed.go

@@ -13,7 +13,7 @@ type Point struct {
 }
 
 var (
-	threshold float64 = 10
+	threshold float64 = 5
 	IsCurve   bool
 	count1    int64
 	//定义园区T字路口的经纬度坐标值
@@ -78,8 +78,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 		IsCurve = IfEnter(pointlist, 16.0, data.Latitude, data.Longitude)
 
 		if Automode == 1 && IsCurve {
-			absspeed := math.Sqrt(math.Pow(data.VelocityX, 2) + math.Pow(data.VelocityY, 2))
-			if absspeed >= threshold {
+			absspeed, ok := shareVars.Load("AbsSpeed")
+			if ok && absspeed.(float64) >= threshold {
 				eventLabel := "JunctionOverspeed"
 				fmt.Println(eventLabel)
 				count1 = 1

+ 2 - 2
trigger/pjisuv/cicv_location/OverSpeed/main/OverSpeed.go

@@ -24,8 +24,8 @@ func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string
 	//threshold := 65.0
 	OutsideWorkshopFlag, _ := shareVars.Load("OutsideWorkshopFlag")
 	OutsideWorkshopFlag = OutsideWorkshopFlag.(bool)
-	threshold := 9999.0
-	if math.Pow(math.Pow(data.VelocityX, 2)+math.Pow(data.VelocityY, 2), 0.5)*3.6 >= threshold && OutsideWorkshopFlag == true {
+	threshold := 10.0
+	if math.Pow(math.Pow(data.VelocityX, 2)+math.Pow(data.VelocityY, 2), 0.5) >= threshold && OutsideWorkshopFlag == true {
 		return "OverSpeed"
 	} else {
 		return ""

+ 90 - 0
trigger/pjisuv/cicv_location/SpeedBumpOverspeed/main/SpeedBumpOverspeed.go

@@ -0,0 +1,90 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_msgs"
+	"fmt"
+	"math"
+	"sync"
+)
+
+type Point struct {
+	Latitude  float64
+	Longitude float64
+}
+
+var (
+	threshold   float64 = 4.0
+	IsSpeedBump bool
+	count1      int64
+	//定义园减速带经纬度
+	point3     = Point{39.73002669559404, 116.49020088105833}
+	point4     = Point{39.728688903362254, 116.4896802037327}
+	point5     = Point{39.728406077854494, 116.48901074348075}
+	point6     = Point{39.7287051534828, 116.48822784823321}
+	point7     = Point{39.72925799042968, 116.49074368869654}
+	pointlist1 = []Point{point3, point4, point5, point6, point7}
+)
+
+func Topic() string {
+	return "/cicv_location"
+}
+
+// 禁止存在下划线_
+func Label() string {
+	return "SpeedBumpOverspeed"
+}
+
+func IfEnter(pointlist []Point, radius float64, lat, lon float64) bool {
+	// 判断是否进入点列表中的区域
+	point1 := Point{Latitude: lat, Longitude: lon}
+	for _, point := range pointlist {
+		d := distance(point1, point)
+		if d <= radius {
+			return true
+		}
+	}
+	return false
+}
+
+// 计算两点之间的距离(米)
+func distance(point1, point2 Point) 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
+}
+
+func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	if count1%10 == 0 {
+		Automode, _ := shareVars.Load("AutomodeOfPjVehicleFdbPub")
+		Automode = Automode.(int16)
+		IsSpeedBump = IfEnter(pointlist1, 7.0, data.Latitude, data.Longitude)
+
+		if Automode == 1 && IsSpeedBump {
+			absspeed, ok := shareVars.Load("AbsSpeed")
+			if ok && absspeed.(float64) >= threshold {
+				eventLabel := "SpeedBumpOverspeed"
+				fmt.Println(eventLabel)
+				count1 = 1
+				return Label()
+			}
+		}
+	}
+	count1++
+	return ""
+}

+ 83 - 0
trigger/pjisuv/cicv_location/TargetCarBehindWhenReversing/main/TargetCarBehindWhenReversing.go

@@ -0,0 +1,83 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_msgs"
+	"fmt"
+	"math"
+	"sync"
+)
+
+type Point struct {
+	Latitude  float64
+	Longitude float64
+}
+
+var (
+	count1 int64
+)
+
+func Topic() string {
+	return "/cicv_location"
+}
+
+// 禁止存在下划线_
+func Label() string {
+	return "TargetCarBehindWhenReversing"
+}
+
+func IfObstaclesNearby(shareVars *sync.Map) bool {
+	ObjDicOfTpperception, ok1 := shareVars.Load("objDicOfTpperception")
+	ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
+	if ok1 {
+		for _, obj := range ObjDic {
+			if obj[0][len(obj[0])-1] >= -15 && obj[0][len(obj[0])-1] <= -1 && (math.Abs(float64(obj[1][len(obj[1])-1]))) <= 6 {
+				return true
+
+			}
+		}
+
+	}
+	return false
+}
+func calculateDirectionAngle(speedX, speedY float64) float64 {
+	// 使用反正切函数计算方向角
+	angle := math.Atan2(speedY, speedX)
+
+	// 将角度转换为度数
+	angleDegree := angle * (180 / math.Pi)
+
+	// 将角度限定在 0~360 范围内
+	if angleDegree < 0 {
+		angleDegree += 360
+	} else if angleDegree > 360 {
+		angleDegree -= 360
+	}
+
+	return angleDegree
+}
+func Rule(shareVars *sync.Map, data *pjisuv_msgs.PerceptionLocalization) string {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	if count1%100 == 0 {
+		OutsideWorkshopFlag, _ := shareVars.Load("OutsideWorkshopFlag")
+		OutsideWorkshopFlag = OutsideWorkshopFlag.(bool)
+		directionAngle := calculateDirectionAngle(data.VelocityX, data.VelocityY)
+		diffAngle := math.Abs(float64(directionAngle - data.Yaw))
+		if OutsideWorkshopFlag == true {
+			AbsSpeed, _ := shareVars.Load("AbsSpeed")
+			flag := IfObstaclesNearby(shareVars)
+			if flag && AbsSpeed.(float64) >= 1 && diffAngle >= 160 && diffAngle <= 200 {
+				eventLabel := "TargetCarBehindWhenReversing"
+				fmt.Println(eventLabel)
+				count1 = 1
+				return Label()
+			}
+
+		}
+	}
+	count1++
+	return ""
+}

+ 15 - 8
trigger/pjisuv/cicv_ticker/BeOvertakenInCorner/main/BeOvertakenInCorner.go

@@ -52,13 +52,18 @@ func Rule(shareVars *sync.Map) {
 		}
 	}(shareVars)
 }
-func isBrake(ObjectList [][]float32) bool {
-	for i, speed := range ObjectList[3] {
+func IsOvertaken(AbsSpeed float32, ObjectList [][]float32) bool {
 
-		if math.Abs(float64(ObjectList[1][i])) <= 1.3 && speed >= 3 && ObjectList[0][i] >= 1.3 {
-			for j := 0; j < len(ObjectList[0])-i-1; j++ {
-				if math.Abs(float64(ObjectList[1][1+i+j])) <= 1.3 && ObjectList[3][1+i+j] <= 1 {
-					return true
+	if AbsSpeed > 1.0 {
+		//fmt.Println("yes")
+		//fmt.Println(ObjectList)
+		for i, objX := range ObjectList[0] {
+
+			if math.Abs(float64(ObjectList[1][i])) <= 8 && objX <= -2 {
+				for j := 0; j < len(ObjectList[0])-i-1; j++ {
+					if math.Abs(float64(ObjectList[1][1+i+j])) <= 8 && ObjectList[0][1+i+j] >= 2.5 {
+						return true
+					}
 				}
 			}
 		}
@@ -99,19 +104,21 @@ func FinalCallback(shareVars *sync.Map) {
 
 	Latitude, ok1 := shareVars.Load("Latitude")
 	Longitude, ok2 := shareVars.Load("Longitude")
+	AbsSpeed, ok3 := shareVars.Load("AbsSpeed")
 
-	if ok && ok1 && ok2 && IfEnter(pointlist1, 50, Latitude.(float64), Longitude.(float64)) {
+	if ok && ok1 && ok2 && ok3 && IfEnter(pointlist1, 50, Latitude.(float64), Longitude.(float64)) {
 		ObjDicOfTpperception, okn := shareVars.Load("objDicOfTpperception")
 		ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
 
 		if okn && OutsideWorkshopFlag.(bool) == true {
 			for _, objValue := range ObjDic {
 				Maxlenobj = max(Maxlenobj, int32(len(objValue[0])))
-				if len(ObjDic[0]) <= 10 || !isBrake(objValue) {
+				if len(ObjDic[0]) <= 10 || !IsOvertaken(AbsSpeed.(float32), objValue) {
 					continue
 				}
 				event_lable := "BeOvertakenInCorner"
 				fmt.Println(event_lable)
+				ObjDicOfTpperception = make(map[uint32][][]float32)
 				pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 			}
 			if Maxlenobj >= 100 {

+ 92 - 0
trigger/pjisuv/cicv_ticker/BeOvertakenWithHighSpeed/main/BeOvertakenWithHighSpeed.go

@@ -0,0 +1,92 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_ticker"
+	"fmt"
+	"math"
+	"sync"
+	"time"
+)
+
+var (
+	Maxlenobj int32   = 0
+	threshold float64 = 5.0
+)
+
+// 定时任务触发器固定的
+func Topic() string {
+	return pjisuv_ticker.TickerTopic
+}
+
+// ******* 禁止存在下划线_
+// 触发器标记
+func Label() string {
+	return "BeOvertakenWithHighSpeed"
+}
+
+func Rule(shareVars *sync.Map) {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	// 1 使用goroutine
+	go func(shareVars *sync.Map) {
+		// 2 定义触发器的间隔时间
+		ticker := time.NewTicker(time.Duration(2) * time.Second)
+		defer ticker.Stop()
+		// 3 运行一个无限循环
+		for {
+			select {
+			// 定时器触发时执行的代码
+			case <-ticker.C:
+				FinalCallback(shareVars)
+
+			}
+		}
+	}(shareVars)
+}
+func IsOvertaken(ObjectList [][]float32) bool {
+	//fmt.Println("yes")
+	//fmt.Println(ObjectList)
+	for i, objX := range ObjectList[0] {
+
+		if math.Abs(float64(ObjectList[1][i])) <= 4 && objX <= -2 {
+			for j := 0; j < len(ObjectList[0])-i-1; j++ {
+				if math.Abs(float64(ObjectList[1][1+i+j])) <= 4 && ObjectList[0][1+i+j] >= 2.5 {
+					return true
+				}
+			}
+		}
+	}
+
+	return false
+}
+
+func FinalCallback(shareVars *sync.Map) {
+	OutsideWorkshopFlag, ok := shareVars.Load("OutsideWorkshopFlag")
+	AbsSpeed, ok3 := shareVars.Load("AbsSpeed")
+
+	if ok && ok3 && AbsSpeed.(float64) >= threshold {
+		ObjDicOfTpperception, okn := shareVars.Load("objDicOfTpperception")
+		ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
+
+		if okn && OutsideWorkshopFlag.(bool) == true {
+			for _, objValue := range ObjDic {
+				Maxlenobj = max(Maxlenobj, int32(len(objValue[0])))
+				if len(ObjDic[0]) <= 10 || !IsOvertaken(objValue) {
+					continue
+				}
+				event_lable := "BeOvertakenWithHighSpeed"
+				fmt.Println(event_lable)
+				pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
+			}
+			if Maxlenobj >= 100 {
+				ObjDicOfTpperception = make(map[uint32][][]float32)
+				shareVars.Store("ObjDicOfTpperception", ObjDicOfTpperception)
+				Maxlenobj = 0
+			}
+		}
+	}
+
+}

+ 96 - 0
trigger/pjisuv/cicv_ticker/FrontCarDrivingWrongDirection/main/FrontCarDrivingWrongDirection.go

@@ -0,0 +1,96 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_ticker"
+	"fmt"
+	"math"
+	"sync"
+	"time"
+)
+
+var (
+	Maxlenobj int32 = 0
+)
+
+// 定时任务触发器固定的
+func Topic() string {
+	return pjisuv_ticker.TickerTopic
+}
+
+// ******* 禁止存在下划线_
+// 触发器标记
+func Label() string {
+	return "FrontCarDrivingWrongDirection"
+}
+
+func Rule(shareVars *sync.Map) {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	// 1 使用goroutine
+	go func(shareVars *sync.Map) {
+		// 2 定义触发器的间隔时间
+		ticker := time.NewTicker(time.Duration(2) * time.Second)
+		defer ticker.Stop()
+		// 3 运行一个无限循环
+		for {
+			select {
+			// 定时器触发时执行的代码
+			case <-ticker.C:
+				FinalCallback(shareVars)
+
+			}
+		}
+	}(shareVars)
+}
+
+func isWrongDirection(ObjectList [][]float32, AngularVelocityZ float64, YawOfCicvLocation float32) bool {
+
+	for i, heading := range ObjectList[4] {
+		objXi := ObjectList[0][i]
+		objYi := ObjectList[1][i]
+		Anglei := math.Abs(float64(heading - YawOfCicvLocation))
+
+		if math.Abs(float64(objYi)) <= 1.8 && math.Abs(AngularVelocityZ) <= 0.6 && objXi > 15 && Anglei <= 200 && Anglei >= 160 {
+			//fmt.Println(objY)
+			for j := 0; j < len(ObjectList[1])-i-1; j++ {
+				objXij := ObjectList[0][1+i+j]
+				objYij := ObjectList[1][1+i+j]
+				if math.Abs(float64(objYij)) <= 1.8 && math.Abs(AngularVelocityZ) <= 0.6 && objXij <= 10 {
+					return true
+				}
+			}
+		}
+	}
+	return false
+}
+func FinalCallback(shareVars *sync.Map) {
+	OutsideWorkshopFlag, ok := shareVars.Load("OutsideWorkshopFlag")
+	ObjDicOfTpperception, ok1 := shareVars.Load("objDicOfTpperception")
+	YawOfCicvLocation, ok2 := shareVars.Load("YawOfCicvLocation")
+	ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
+	AngularVelocityZOfCicvLocation, _ := shareVars.Load("AngularVelocityZOfCicvLocation")
+	AngularVelocityZ := AngularVelocityZOfCicvLocation.(float64)
+
+	if ok && ok1 && ok2 && OutsideWorkshopFlag.(bool) == true {
+		for _, objValue := range ObjDic {
+			Maxlenobj = max(Maxlenobj, int32(len(objValue[0])))
+			if len(ObjDic[0]) <= 10 || !isWrongDirection(objValue, AngularVelocityZ, YawOfCicvLocation.(float32)) {
+				continue
+			}
+			event_lable := "FrontCarDrivingWrongDirection"
+			fmt.Println(event_lable)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
+			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
+		}
+
+		if Maxlenobj >= 100 {
+			ObjDicOfTpperception = make(map[uint32][][]float32)
+			shareVars.Store("ObjDicOfTpperception", ObjDicOfTpperception)
+			Maxlenobj = 0
+		}
+	}
+
+}

+ 1 - 0
trigger/pjisuv/cicv_ticker/FrontVehicleBrake/main/FrontVehicleBrake.go

@@ -72,6 +72,7 @@ func FinalCallback(shareVars *sync.Map) {
 			}
 			event_lable := "FrontVehicleBrake"
 			fmt.Println(event_lable)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}
 		if Maxlenobj >= 100 {

+ 127 - 0
trigger/pjisuv/cicv_ticker/FrontVehicleBrakeInCurve/main/FrontVehicleBrakeInCurve.go

@@ -0,0 +1,127 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_ticker"
+	"fmt"
+	"math"
+	"sync"
+	"time"
+)
+
+type Point struct {
+	Latitude  float64
+	Longitude float64
+}
+
+var (
+	Maxlenobj  int32 = 0
+	pointcurve       = Point{39.73004426154644, 116.49248639463602}
+	pointlist1       = []Point{pointcurve}
+)
+
+// 定时任务触发器固定的
+func Topic() string {
+	return pjisuv_ticker.TickerTopic
+}
+
+// ******* 禁止存在下划线_
+// 触发器标记
+func Label() string {
+	return "FrontVehicleBrakeInCurve"
+}
+func IfEnter(pointlist []Point, radius float64, lat, lon float64) bool {
+	// 判断是否进入点列表中的区域
+	point1 := Point{Latitude: lat, Longitude: lon}
+	for _, point := range pointlist {
+		d := distance(point1, point)
+		if d <= radius {
+			return true
+		}
+	}
+	return false
+}
+
+// 计算两点之间的距离(米)
+func distance(point1, point2 Point) 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
+}
+
+func Rule(shareVars *sync.Map) {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	// 1 使用goroutine
+
+	go func(shareVars *sync.Map) {
+		// 2 定义触发器的间隔时间
+		ticker := time.NewTicker(time.Duration(3) * time.Second)
+		defer ticker.Stop()
+		// 3 运行一个无限循环
+		for {
+			select {
+			// 定时器触发时执行的代码
+			case <-ticker.C:
+				FinalCallback(shareVars)
+
+			}
+		}
+	}(shareVars)
+}
+func isBrake(ObjectList [][]float32) bool {
+	for i, speed := range ObjectList[3] {
+
+		if math.Abs(float64(ObjectList[1][i])) <= 5.3 && speed >= 6/3.6 && ObjectList[0][i] >= 1.3 {
+			for j := 0; j < len(ObjectList[0])-i-1; j++ {
+				if math.Abs(float64(ObjectList[1][1+i+j])) <= 5.3 && ObjectList[3][1+i+j] <= 1/3.6 {
+					return true
+				}
+			}
+		}
+	}
+	return false
+}
+
+func FinalCallback(shareVars *sync.Map) {
+	OutsideWorkshopFlag, ok := shareVars.Load("OutsideWorkshopFlag")
+	ObjDicOfTpperception, ok1 := shareVars.Load("objDicOfTpperception")
+	Latitude, ok2 := shareVars.Load("Latitude")
+	Longitude, ok3 := shareVars.Load("Longitude")
+	ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
+
+	if ok && ok1 && ok2 && ok3 && OutsideWorkshopFlag.(bool) == true {
+		enterflag := IfEnter(pointlist1, 30.0, Latitude.(float64), Longitude.(float64))
+		if enterflag {
+			for _, objValue := range ObjDic {
+				Maxlenobj = max(Maxlenobj, int32(len(objValue[0])))
+				if len(ObjDic[0]) <= 10 || !isBrake(objValue) {
+					continue
+				}
+				event_lable := "FrontVehicleBrakeInCurve"
+				fmt.Println(event_lable)
+				ObjDicOfTpperception = make(map[uint32][][]float32)
+				pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
+			}
+		}
+		if Maxlenobj >= 100 {
+			ObjDicOfTpperception = make(map[uint32][][]float32)
+			shareVars.Store("ObjDicOfTpperception", ObjDicOfTpperception)
+			Maxlenobj = 0
+		}
+	}
+
+}

+ 134 - 0
trigger/pjisuv/cicv_ticker/FrontVehicleBrakeInJunction/main/FrontVehicleBrakeInJunction.go

@@ -0,0 +1,134 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_ticker"
+	"fmt"
+	"math"
+	"sync"
+	"time"
+)
+
+type Point struct {
+	Latitude  float64
+	Longitude float64
+}
+
+var (
+	Maxlenobj int32 = 0
+	//定义园区T字路口的经纬度坐标值
+	point3 = Point{39.73040966605621, 116.48995329696209}
+	point4 = Point{39.73083727413453, 116.49079780188244}
+	point5 = Point{39.72976753711939, 116.49043130389033}
+	point6 = Point{39.73012466515933, 116.49128381717591}
+	point7 = Point{39.729251498328246, 116.49077484625299}
+	point8 = Point{39.72964529630643, 116.49164592200161}
+
+	PointJunctionList = []Point{point3, point4, point5, point6, point7, point8}
+)
+
+// 定时任务触发器固定的
+func Topic() string {
+	return pjisuv_ticker.TickerTopic
+}
+
+// ******* 禁止存在下划线_
+// 触发器标记
+func Label() string {
+	return "FrontVehicleBrakeInJunction"
+}
+func IfEnter(pointlist []Point, radius float64, lat, lon float64) bool {
+	// 判断是否进入点列表中的区域
+	point1 := Point{Latitude: lat, Longitude: lon}
+	for _, point := range pointlist {
+		d := distance(point1, point)
+		if d <= radius {
+			return true
+		}
+	}
+	return false
+}
+
+// 计算两点之间的距离(米)
+func distance(point1, point2 Point) 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
+}
+
+func Rule(shareVars *sync.Map) {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	// 1 使用goroutine
+
+	go func(shareVars *sync.Map) {
+		// 2 定义触发器的间隔时间
+		ticker := time.NewTicker(time.Duration(3) * time.Second)
+		defer ticker.Stop()
+		// 3 运行一个无限循环
+		for {
+			select {
+			// 定时器触发时执行的代码
+			case <-ticker.C:
+				FinalCallback(shareVars)
+
+			}
+		}
+	}(shareVars)
+}
+func isBrake(ObjectList [][]float32) bool {
+	for i, speed := range ObjectList[3] {
+
+		if math.Abs(float64(ObjectList[1][i])) <= 5.3 && speed >= 6/3.6 && ObjectList[0][i] >= 1.3 {
+			for j := 0; j < len(ObjectList[0])-i-1; j++ {
+				if math.Abs(float64(ObjectList[1][1+i+j])) <= 5.3 && ObjectList[3][1+i+j] <= 1/3.6 {
+					return true
+				}
+			}
+		}
+	}
+	return false
+}
+
+func FinalCallback(shareVars *sync.Map) {
+	OutsideWorkshopFlag, ok := shareVars.Load("OutsideWorkshopFlag")
+	ObjDicOfTpperception, ok1 := shareVars.Load("objDicOfTpperception")
+	Latitude, ok2 := shareVars.Load("Latitude")
+	Longitude, ok3 := shareVars.Load("Longitude")
+	ObjDic := ObjDicOfTpperception.(map[uint32][][]float32)
+
+	if ok && ok1 && ok2 && ok3 && OutsideWorkshopFlag.(bool) == true {
+		enterflag := IfEnter(PointJunctionList, 20.0, Latitude.(float64), Longitude.(float64))
+		if enterflag {
+			for _, objValue := range ObjDic {
+				Maxlenobj = max(Maxlenobj, int32(len(objValue[0])))
+				if len(ObjDic[0]) <= 10 || !isBrake(objValue) {
+					continue
+				}
+				event_lable := "FrontVehicleBrakeInJunction"
+				fmt.Println(event_lable)
+				ObjDicOfTpperception = make(map[uint32][][]float32)
+				pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
+			}
+		}
+		if Maxlenobj >= 100 {
+			ObjDicOfTpperception = make(map[uint32][][]float32)
+			shareVars.Store("ObjDicOfTpperception", ObjDicOfTpperception)
+			Maxlenobj = 0
+		}
+	}
+
+}

+ 2 - 1
trigger/pjisuv/cicv_ticker/FrontVehicleCutInFar/main/FrontVehicleCutInFar.go

@@ -49,7 +49,7 @@ func isCuttingIn(ObjectList [][]float32, AngularVelocityZ float64) bool {
 
 	for i, objY := range ObjectList[1] {
 
-		if math.Abs(float64(objY)) >= 1.3 && math.Abs(AngularVelocityZ) <= 0.6 && ObjectList[0][i] >= 2 {
+		if math.Abs(float64(objY)) >= 1.8 && math.Abs(AngularVelocityZ) <= 0.6 && ObjectList[0][i] >= 2 {
 			//fmt.Println(objY)
 			for j := 0; j < len(ObjectList[1])-i-1; j++ {
 				objX := ObjectList[0][1+i+j]
@@ -77,6 +77,7 @@ func FinalCallback(shareVars *sync.Map) {
 			}
 			event_lable := "FrontVehicleCutInFar"
 			fmt.Println(event_lable)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}
 

+ 2 - 1
trigger/pjisuv/cicv_ticker/FrontVehicleCutInNear/main/FrontVehicleCutInNear.go

@@ -49,7 +49,7 @@ func isCuttingIn(ObjectList [][]float32, AngularVelocityZ float64) bool {
 
 	for i, objY := range ObjectList[1] {
 
-		if math.Abs(float64(objY)) >= 1.3 && math.Abs(AngularVelocityZ) <= 0.6 && ObjectList[0][i] >= 2 {
+		if math.Abs(float64(objY)) >= 1.8 && math.Abs(AngularVelocityZ) <= 0.6 && ObjectList[0][i] >= 2 {
 			//fmt.Println(objY)
 			for j := 0; j < len(ObjectList[1])-i-1; j++ {
 				objX := ObjectList[0][1+i+j]
@@ -78,6 +78,7 @@ func FinalCallback(shareVars *sync.Map) {
 			}
 			event_lable := "FrontVehicleCutInNear"
 			fmt.Println(event_lable)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}
 

+ 1 - 1
trigger/pjisuv/cicv_ticker/FrontVehicleCutOutFar/main/FrontVehicleCutOutFar.go

@@ -77,7 +77,7 @@ func FinalCallback(shareVars *sync.Map) {
 			}
 			event_lable := "FrontVehicleCutOutFar"
 			fmt.Println(event_lable)
-			//ObjDicOfTpperception = make(map[uint32][][]float32)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}
 

+ 1 - 0
trigger/pjisuv/cicv_ticker/FrontVehicleCutOutNear/main/FrontVehicleCutOutNear.go

@@ -77,6 +77,7 @@ func FinalCallback(shareVars *sync.Map) {
 			}
 			event_lable := "FrontVehicleCutOutNear"
 			fmt.Println(event_lable)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}
 

+ 71 - 0
trigger/pjisuv/cicv_ticker/GearJump/main/GearJump.go

@@ -0,0 +1,71 @@
+package main
+
+import (
+	"cicv-data-closedloop/pjisuv_ticker"
+	"fmt"
+	"sync"
+	"time"
+)
+
+// 定时任务触发器固定的
+func Topic() string {
+	return pjisuv_ticker.TickerTopic
+}
+
+// ******* 禁止存在下划线_
+// 触发器标记
+
+func Label() string {
+	return "GearJump"
+}
+
+func Rule(shareVars *sync.Map) {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered from panic:", r)
+		}
+	}()
+	// 1 使用goroutine
+	go func(shareVars *sync.Map) {
+		// 2 定义触发器的间隔时间
+		ticker := time.NewTicker(time.Duration(2) * time.Second)
+		defer ticker.Stop()
+		// 3 运行一个无限循环
+		for {
+			select {
+			// 定时器触发时执行的代码
+			case <-ticker.C:
+				FinalCallback(shareVars)
+
+			}
+		}
+	}(shareVars)
+}
+func countChanges(slice []int16) int {
+	count := 0
+	for i := 0; i < len(slice)-1; i++ {
+		if slice[i] != slice[i+1] {
+			count++
+		}
+	}
+	return count
+}
+
+func FinalCallback(shareVars *sync.Map) {
+	OutsideWorkshopFlag, ok := shareVars.Load("OutsideWorkshopFlag")
+	GearPosSlice, ok1 := shareVars.Load("GearPosSlice")
+
+	if ok && ok1 && OutsideWorkshopFlag.(bool) == true {
+		count := countChanges(GearPosSlice.([]int16))
+		if count >= 3 {
+			event_lable := "GearJump"
+			fmt.Println(event_lable)
+			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
+		}
+
+		GearPosSlice = []int16{}
+		shareVars.Store("GearPosSlice", GearPosSlice)
+
+	}
+
+}

+ 1 - 2
trigger/pjisuv/cicv_ticker/HuaLong/main/HuaLong.go

@@ -40,7 +40,6 @@ func Rule(shareVars *sync.Map) {
 			// 定时器触发时执行的代码
 			case <-ticker.C:
 				FinalCallback(shareVars)
-
 			}
 		}
 	}(shareVars)
@@ -77,7 +76,7 @@ func FinalCallback(shareVars *sync.Map) {
 			fmt.Println(event_lable)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}
-		if len(egoSteeringRealOfDataRead.([]float64)) >= 600 {
+		if len(egoSteeringRealOfDataRead.([]float64)) >= 800 {
 			egoSteeringRealOfDataRead = []float64{}
 			shareVars.Store("egoSteeringRealOfDataRead", egoSteeringRealOfDataRead)
 		}

+ 3 - 2
trigger/pjisuv/cicv_ticker/OutOperationZone/main/OutOperationZone.go

@@ -107,9 +107,10 @@ func Rule(shareVars *sync.Map) {
 func FinalCallback(shareVars *sync.Map) {
 	PositionX, ok1 := shareVars.Load("PositionXOfCicvLocation")
 	PositionY, ok2 := shareVars.Load("PositionYOfCicvLocation")
-	if ok1 && ok2 {
+	AbsSpeed, ok3 := shareVars.Load("AbsSpeed")
+	if ok1 && ok2 && ok3 {
 		p := Point{x: PositionX.(float64), y: PositionY.(float64)}
-		if !isPointInPolygon(p, vertices) {
+		if !isPointInPolygon(p, vertices) && AbsSpeed.(float64) >= 1 {
 			eventLabel := "OutOperationZone"
 			fmt.Println(eventLabel)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}

+ 2 - 1
trigger/pjisuv/cicv_ticker/RearVehicleApproach/main/RearVehicleApproach.go

@@ -50,7 +50,7 @@ func isApproach(ObjectList [][]float32) bool {
 
 		if math.Abs(float64(ObjectList[1][i])) <= 1.3 && objX <= -10 {
 			for j := 0; j < len(ObjectList[0])-i-1; j++ {
-				if math.Abs(float64(ObjectList[1][1+i+j])) <= 1.3 && ObjectList[0][1+i+j] >= -8 && ObjectList[2][1+i+j] >= 1 {
+				if math.Abs(float64(ObjectList[1][1+i+j])) <= 1.3 && ObjectList[0][1+i+j] >= -6 && ObjectList[2][1+i+j] >= 1 {
 					return true
 				}
 			}
@@ -71,6 +71,7 @@ func FinalCallback(shareVars *sync.Map) {
 			}
 			event_lable := "RearVehicleApproach"
 			fmt.Println(event_lable)
+			ObjDicOfTpperception = make(map[uint32][][]float32)
 			pjisuv_ticker.TickerChan <- pjisuv_ticker.TickInfo{FaultLabel: Label(), FaultHappenTime: pjisuv_ticker.GetNowTimeCustom()}
 		}