LingxinMeng 1 年之前
父節點
當前提交
120326f724

+ 57 - 2
aarch64/pjisuv/master/service/for_competition.go

@@ -4,19 +4,32 @@ import (
 	commonConfig "cicv-data-closedloop/aarch64/pjisuv/common/config"
 	"cicv-data-closedloop/common/config/c_log"
 	"cicv-data-closedloop/common/util"
+	"github.com/bluenviron/goroslib/v2"
+	"github.com/bluenviron/goroslib/v2/pkg/msgs/std_msgs"
 	"os"
 	"strings"
+	"sync"
 	"time"
 )
 
 var (
-	dir         = "/home/root/competition/"
-	commandArgs = []string{"record", "--split", "--duration=1", "/pji_gps", "/data_read"}
+	dir                    = "/home/root/competition/"
+	commandArgs            = []string{"record", "--split", "--duration=1", "/pji_gps", "/data_read"}
+	topic                  = "/cicv_competition"
+	examBeginUrl           = "3"
+	cacheMutex             sync.Mutex
+	cacheTeamName          map[string]time.Time
+	heartBeatTimeThreshold = 5 * time.Second // 心跳时间
 )
 
 // todo 实车比赛临时使用
 // history record命令无法录制()
 func ForCompetition() {
+	go dataCollection()
+}
+
+// 全量数据采集
+func dataCollection() {
 	c_log.GlobalLogger.Info("开始采集实车算法比赛全量数据。")
 	util.CreateDir(dir)
 	// 1 打包
@@ -46,3 +59,45 @@ func ForCompetition() {
 		}
 	}
 }
+
+// data格式为队伍编号
+// 保存单次考试时间区间
+func examBegin() {
+	_, _ = goroslib.NewSubscriber(goroslib.SubscriberConf{
+		Node:  commonConfig.RosNode,
+		Topic: topic,
+		Callback: func(data *std_msgs.String) {
+			teamName := data.Data
+			cacheMutex.Lock()
+			{
+				if !util.ContainsKey(cacheTeamName, teamName) { // 1 如果缓存数组中没有此队名,代表考试开始,缓存此队名,和当前时间戳
+					cacheTeamName[teamName] = time.Now()
+					// todo 发送http请求到begin接口
+				} else { // 2 如果缓存数组中有此队名,代表考试进行中,刷新时间戳
+					cacheTeamName[teamName] = time.Now()
+				}
+			}
+			cacheMutex.Unlock()
+		},
+	})
+}
+
+func examEnd() {
+	for {
+		time.Sleep(time.Duration(1) * time.Second)
+		cacheMutex.Lock()
+		{
+			keysToDelete := []string{}
+			for teamName, heartBeatTime := range cacheTeamName {
+				if time.Since(heartBeatTime) > heartBeatTimeThreshold { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
+					keysToDelete = append(keysToDelete, teamName)
+				}
+			}
+			for _, key := range keysToDelete { // 检查缓存中的队名,如果超过心跳时间,则代表考试结束,删除缓存中的队名
+				delete(cacheTeamName, key)
+				// todo 发送http请求到end接口
+			}
+		}
+		cacheMutex.Unlock()
+	}
+}

+ 14 - 0
amd64/web_server/entity/e_exam.go

@@ -0,0 +1,14 @@
+package entity
+
+import "time"
+
+type ExamPAO struct {
+	TeamName string `json:"teamName"` // 队伍名字
+}
+
+type ExamPO struct {
+	Id              int       `db:"id"` // 自增id
+	BeginTime       time.Time `db:"begin_time"`
+	EndTime         time.Time `db:"end_time"`
+	ScoreReportPath time.Time `db:"score_report_path"`
+}

+ 75 - 0
amd64/web_server/handler/h_exam.go

@@ -0,0 +1,75 @@
+package handler
+
+import (
+	"cicv-data-closedloop/amd64/web_server/entity"
+	"cicv-data-closedloop/common/config/c_db"
+	"cicv-data-closedloop/common/config/c_log"
+	commonEntity "cicv-data-closedloop/common/entity"
+	"cicv-data-closedloop/common/util"
+	"github.com/gin-gonic/gin"
+	"net/http"
+	"time"
+)
+
+// 考试开始时间
+func Begin(c *gin.Context) {
+	param := new(entity.ExamPAO)
+	// 映射到结构体
+	if err := c.ShouldBindJSON(&param); err != nil {
+		c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err)
+		c.JSON(http.StatusBadRequest, commonEntity.Response{
+			Code: 500,
+			Msg:  "请求体解析失败。",
+		})
+		return
+	}
+	// 插入到数据库
+	sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["insert_exam.sql"])
+	if err := c_db.DoTx(sqlTemplate, []any{
+		param.TeamName,
+		time.Now(),
+	}); err != nil {
+		c_log.GlobalLogger.Error("插入数据报错:", err)
+		c.JSON(http.StatusBadRequest, commonEntity.Response{
+			Code: 500,
+			Msg:  "插入数据报错。",
+		})
+		return
+	}
+	c.JSON(http.StatusOK, commonEntity.Response{
+		Code: 200,
+		Msg:  "插入数据成功。",
+	})
+}
+
+// 考试结束时间
+func End(c *gin.Context) {
+	param := new(entity.ExamPAO)
+	// 映射到结构体
+	if err := c.ShouldBindJSON(&param); err != nil {
+		c_log.GlobalLogger.Error("项目启动接收请求参数报错:", err)
+		c.JSON(http.StatusBadRequest, commonEntity.Response{
+			Code: 500,
+			Msg:  "请求体解析失败。",
+		})
+		return
+	}
+	// 1 查询指定队伍的开始时间最新的考试是否有结束时间,如果有则不在处理,如果没有则更新
+	// 插入到数据库
+	sqlTemplate, _ := util.ReadFile(c_db.SqlFilesMap["update.sql"])
+	if err := c_db.DoTx(sqlTemplate, []any{
+		time.Now(),
+		param.TeamName,
+	}); err != nil {
+		c_log.GlobalLogger.Error("插入数据报错:", err)
+		c.JSON(http.StatusBadRequest, commonEntity.Response{
+			Code: 500,
+			Msg:  "插入数据报错。",
+		})
+		return
+	}
+	c.JSON(http.StatusOK, commonEntity.Response{
+		Code: 200,
+		Msg:  "插入数据成功。",
+	})
+}

+ 3 - 0
amd64/web_server/main.go

@@ -68,6 +68,9 @@ func main() {
 	router.Use(middleware.ValidateHeaders(ApplicationYaml.Web.WhiteList, ApplicationYaml.Web.Token)) // 全局请求头校验
 	// 通过路由组设置全局前缀
 	projectPrefix := router.Group(ApplicationYaml.Web.RoutePrefix)
+	examPrefix := projectPrefix.Group("/exam")
+	examPrefix.POST("/begin", handler.Page) // 考试开始
+	examPrefix.POST("/end", handler.Page)   // 考试结束
 	reportPrefix := projectPrefix.Group("/report")
 	reportPrefix.POST("/page", handler.Page) // 分页查询
 	//reportPrefix.POST("/pdf", handler.Hello)  // pdf下载

+ 2 - 2
amd64/web_server/sql/insert_device_monitor.sql

@@ -1,2 +1,2 @@
-insert into device_monitor(total_cpu_usage, total_memory_usage, top10_process,device_number,soc_ip,report_time)
-values (?,?,?,?,?,?)
+insert into device_monitor(total_cpu_usage, total_memory_usage, top10_process, device_number, soc_ip, report_time)
+values (?, ?, ?, ?, ?, ?)

+ 2 - 0
amd64/web_server/sql/insert_exam.sql

@@ -0,0 +1,2 @@
+insert into device_monitor(team_name, begin_time)
+values (?, ?)

+ 2 - 0
amd64/web_server/sql/update_exam.sql

@@ -0,0 +1,2 @@
+update exam set end_time = ? where team_name = ?
+

+ 12 - 0
common/util/u_map.go

@@ -0,0 +1,12 @@
+package util
+
+import "time"
+
+// 函数名为 ContainsKey,参数为一个 map 和一个 key
+func ContainsKey(m map[string]time.Time, key string) bool {
+	// 通过 map[key] 来访问 map 中的值
+	// 如果 map[key] 返回的值存在,则表示该 key 存在于 map 中,返回 true
+	// 如果 map[key] 返回的值不存在,则表示该 key 不存在于 map 中,返回 false
+	_, exists := m[key]
+	return exists
+}

+ 19 - 0
common/util/u_slice.go

@@ -1,5 +1,10 @@
 package util
 
+import (
+	"errors"
+	"reflect"
+)
+
 // AppendIfNotExists 向切片中追加元素,如果元素已存在则不添加
 func AppendIfNotExists(slice []string, element string) []string {
 	for _, item := range slice {
@@ -26,3 +31,17 @@ func MergeSlice(slice1 []string, slice2 []string) []string {
 	}
 	return slice1
 }
+
+func ContainsElement(slice interface{}, element interface{}) (bool, error) {
+	sliceValue := reflect.ValueOf(slice)
+	if sliceValue.Kind() != reflect.Slice {
+		return false, errors.New("没有输入切片。")
+	}
+	for i := 0; i < sliceValue.Len(); i++ {
+		currentElement := sliceValue.Index(i).Interface()
+		if reflect.DeepEqual(currentElement, element) {
+			return true, nil
+		}
+	}
+	return false, nil
+}