孟令鑫 hace 1 año
padre
commit
c6199b14b5

+ 5 - 0
build-all.sh

@@ -1,8 +1,13 @@
 go build -o ./kinglong-master.exe ./kinglong/master/main/master.go
 go build -o ./kinglong-slave.exe ./kinglong/master/main/master.go
 go build -o ./kinglong-control.exe ./kinglong/master/main/master.go
+
 go build -o ./pji-master.exe ./pji/master/main/master.go
 go build -o ./pji-control.exe ./pji/control/main/control.go
+
+go build -o ./plugin-vaildate.exe ./plugin-vaildate/main/main.go
+go build -o ./compile-service.exe ./compile-service/main/main.go
+
 go build --buildmode=plugin -o ./cpuoveroccupied.so ./trigger/cpuoveroccupied/main/cpuoveroccupied.go
 go build --buildmode=plugin -o ./detectfault.so ./trigger/detectfault/main/detectfault.go
 go build --buildmode=plugin -o ./locationfailed.so ./trigger/locationfailed/main/locationfailed.go

+ 25 - 0
compile-service/main/main.go

@@ -0,0 +1,25 @@
+package main
+
+import (
+	"cicv-data-closedloop/compile-service/package/config/c_log"
+	"cicv-data-closedloop/compile-service/package/config/c_oss"
+	"cicv-data-closedloop/compile-service/package/handler/h_compile_plugin"
+	"cicv-data-closedloop/compile-service/package/handler/h_validate_request_header"
+	"fmt"
+	"net/http"
+)
+
+func init() {
+	// 初始化日志配置
+	c_log.InitLog()
+	c_oss.InitOss()
+}
+
+func main() {
+	mux := http.NewServeMux()
+	mux.Handle("/compile", h_validate_request_header.HeaderValidationMiddleware(&h_compile_plugin.CompileHandler{}))
+	err := http.ListenAndServe(":12340", mux)
+	if err != nil {
+		fmt.Println("Error:", err)
+	}
+}

+ 38 - 0
compile-service/package/config/c_log/log_config.go

@@ -0,0 +1,38 @@
+package c_log
+
+import (
+	"fmt"
+	"github.com/sirupsen/logrus"
+	"os"
+	"path/filepath"
+	"runtime"
+	"time"
+)
+
+var GlobalLogger *logrus.Logger
+
+// InitLog 初始化日志配置
+func InitLog() {
+	initGlobalLogger()
+}
+
+// initGlobalLogger 初始化日志配置
+func initGlobalLogger() {
+	time.Sleep(time.Duration(1) * time.Second)
+	// 创建、追加、读写,777,所有权限
+	f, err := os.OpenFile("program-"+time.Now().Format("2006-01-02-15-04-05")+".log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
+	if err != nil {
+		os.Exit(-1)
+	}
+	GlobalLogger = logrus.New()
+	GlobalLogger.SetOutput(f)
+	GlobalLogger.SetReportCaller(true) // 开启行号显示
+	GlobalLogger.SetFormatter(&logrus.JSONFormatter{
+		CallerPrettyfier: func(frame *runtime.Frame) (string, string) {
+			fileName := filepath.Base(frame.File)
+			return "", fmt.Sprintf("%s:%d", fileName, frame.Line)
+		},
+	})
+	GlobalLogger.Info("初始化GlobalLogger - 成功")
+
+}

+ 40 - 0
compile-service/package/config/c_oss/oss_config.go

@@ -0,0 +1,40 @@
+package c_oss
+
+import "os"
+
+import (
+	"fmt"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+type OssConnectInfoStruct struct {
+	Endpoint        string `json:"endpoint"`
+	AccessKeyId     string `json:"accessKeyId"`
+	AccessKeySecret string `json:"accessKeySecret"`
+	BucketName      string `json:"bucketName"`
+}
+
+var (
+	cname           = "http://open-bucket.oss.icvdc.com"
+	endpoint        = "oss-cn-beijing-gqzl-d01-a.ops.gqzl-cloud.com"
+	accessKeyId     = "n8glvFGS25MrLY7j"
+	accessKeySecret = "xZ2Fozoarpfw0z28FUhtg8cu0yDc5d"
+	bucketName      = "open-bucket"
+	OssClient       *oss.Client
+	OssBucket       *oss.Bucket
+)
+
+func InitOss() {
+	var err error
+	//OssClient, err = oss.New(cname, accessKeyId, accessKeySecret, oss.UseCname(true)) // 公网
+	OssClient, err = oss.New(endpoint, accessKeyId, accessKeySecret, oss.UseCname(false)) // 内网
+	if err != nil {
+		fmt.Println("无法创建阿里云client:", err)
+		os.Exit(-1)
+	}
+	OssBucket, err = OssClient.Bucket(bucketName)
+	if err != nil {
+		fmt.Println("无法创建阿里云bucket:", err)
+		os.Exit(-1)
+	}
+}

+ 7 - 0
compile-service/package/entity/http_result.go

@@ -0,0 +1,7 @@
+package entity
+
+type HttpResult struct {
+	Status  bool   `json:"status"`
+	Code    string `json:"code"`
+	Message string `json:"message"`
+}

+ 117 - 0
compile-service/package/handler/h_compile_plugin/compile_plugin.go

@@ -0,0 +1,117 @@
+package h_compile_plugin
+
+import (
+	"cicv-data-closedloop/compile-service/package/config/c_log"
+	"cicv-data-closedloop/compile-service/package/config/c_oss"
+	"cicv-data-closedloop/compile-service/package/entity"
+	"cicv-data-closedloop/compile-service/package/util"
+	"encoding/json"
+	"fmt"
+	"github.com/google/uuid"
+	"net/http"
+	"os"
+)
+
+var (
+	compileCmd     = "go"
+	compileCmdArgs = []string{"build", "--buildmode=plugin"}
+	codeDir        = "/root/cicv-data-closedloop/trigger/"
+	// /root/rosbag-handle-compile/plugin-vaildate/plugin-vaildate.exe /root/rosbag-handle-pji/trigger/431a4254/main/431a4254.so
+	vaildateCmd = "/root/rosbag-handle-compile/plugin-vaildate.exe"
+)
+
+// Device 设备
+type Device struct {
+	Name    string
+	CodeDir string
+}
+
+// CompileHandler implements http.Handler
+type CompileHandler struct{}
+
+func (h *CompileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+
+	// 1 校验参数
+	queryParams := r.URL.Query()
+	//deviceType := queryParams.Get("deviceType")
+	//if deviceType == "" {
+	//	c_log.GlobalLogger.Error("参数 deviceType 不能为空。")
+	//	result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1003", Message: "参数 deviceType 不能为空。"})
+	//	_, _ = fmt.Fprintf(w, string(result))
+	//	return
+	//}
+	goObjectKey := queryParams.Get("goObjectKey")
+	if goObjectKey == "" {
+		c_log.GlobalLogger.Error("参数 goObjectKey 不能为空。")
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1003", Message: "参数 goObjectKey 不能为空。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+	soObjectKey := queryParams.Get("soObjectKey")
+	if soObjectKey == "" {
+		c_log.GlobalLogger.Error("参数 soObjectKey 不能为空。")
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1003", Message: "参数 soObjectKey 不能为空。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+	//c_log.GlobalLogger.Infof("接收到参数【deviceType】=%v,【goObjectKey】=%v,【soObjectKey】=%v", deviceType, goObjectKey, soObjectKey)
+	c_log.GlobalLogger.Infof("接收到参数【goObjectKey】=%v,【soObjectKey】=%v", goObjectKey, soObjectKey)
+
+	// 2 从 oss 上下载 go 文件
+	tempMiddlePath := uuid.New().String()[:8]
+	goLocalPath := codeDir + tempMiddlePath + "/main/" + tempMiddlePath + ".go"
+	c_log.GlobalLogger.Infof("下载源代码 %v --> %v", goObjectKey, goLocalPath)
+	if err := util.LimitDownload(c_oss.OssBucket, 41943040, goObjectKey, goLocalPath); err != nil {
+		c_log.GlobalLogger.Error("下载源代码 ", goObjectKey, " 出错:", err)
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1004", Message: "下载源代码 " + goObjectKey + " 出错。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+	// 修改文件权限为 777
+	if err := os.Chmod(goLocalPath, 0777); err != nil {
+		c_log.GlobalLogger.Error("修改文件权限 "+goLocalPath+" 出错:", err)
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1005", Message: "修改文件权限 " + goLocalPath + " 出错。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+
+	// 3 编译 go 文件
+	soLocalPath := codeDir + tempMiddlePath + "/main/" + tempMiddlePath + ".so"
+	copiedCompileCmdArgs := make([]string, len(compileCmdArgs))
+	copy(copiedCompileCmdArgs, compileCmdArgs)
+	copiedCompileCmdArgs = append(copiedCompileCmdArgs, "-o", soLocalPath, goLocalPath)
+	if _, output, err := util.ExecuteWithEnvAndDir(os.Environ(), codeDir, compileCmd, copiedCompileCmdArgs...); err != nil {
+		c_log.GlobalLogger.Error("编译插件 ", goLocalPath, " 出错:【命令】=", compileCmd, " ", copiedCompileCmdArgs, "【output】=", output, ",【err】=", err)
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1006", Message: "编译插件 " + goObjectKey + " 出错。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+
+	// 4 校验插件是否能用
+	if _, output, err := util.Execute(vaildateCmd, soLocalPath); err != nil || output != "0" {
+		c_log.GlobalLogger.Error("插件 ", goLocalPath, " 校验失败:【命令】=", vaildateCmd, " ", soLocalPath, "【output】=", output)
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1007", Message: "插件 " + soObjectKey + " 校验失败。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+	c_log.GlobalLogger.Error("插件 ", soLocalPath, " 校验成功。")
+	// 5 如果插件能用,将插件上传到 oss
+	if err := util.LimitUpload(c_oss.OssBucket, 41943040, soObjectKey, soLocalPath); err != nil {
+		c_log.GlobalLogger.Error("上传插件 ", soObjectKey, " 出错:", err)
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1008", Message: "上传插件 " + soObjectKey + " 出错。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+	// 6 删除本地缓存的源代码和插件
+	if err := util.RemoveDir(codeDir + tempMiddlePath + "/"); err != nil {
+		c_log.GlobalLogger.Error("删除本地缓存目录 ", codeDir+tempMiddlePath+"/", " 出错:", err)
+		result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1009", Message: "删除本地缓存出错。"})
+		_, _ = fmt.Fprintf(w, string(result))
+		return
+	}
+
+	// 7 返回成功数据
+	c_log.GlobalLogger.Error("编译插件 ", goLocalPath, " 成功。")
+	result, _ := json.Marshal(entity.HttpResult{Status: true, Code: "2000", Message: "编译插件" + goObjectKey + "成功。"})
+	_, _ = fmt.Fprintf(w, string(result))
+}

+ 33 - 0
compile-service/package/handler/h_validate_request_header/validate_request_header.go

@@ -0,0 +1,33 @@
+package h_validate_request_header
+
+import (
+	"cicv-data-closedloop/compile-service/package/config/c_log"
+	"cicv-data-closedloop/compile-service/package/entity"
+	"encoding/json"
+	"fmt"
+	"net/http"
+)
+
+var token = "1e8YmQMYwTTbfUYMZczkj1x17AeXjbdU"
+
+// HeaderValidationMiddleware Middleware function to add header validation
+func HeaderValidationMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		acceptToken := r.Header["Authorization"]
+		if _, ok := r.Header["Authorization"]; !ok {
+			c_log.GlobalLogger.Errorf("未添加请求头 Authorization:%v", acceptToken)
+			result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1000", Message: "未添加请求头 Authorization。"})
+			_, _ = fmt.Fprintf(w, string(result))
+			return
+		}
+		if acceptToken[0] != token {
+			c_log.GlobalLogger.Errorf("请求头 Authorization 校验失败:%v --> %v", acceptToken[0], token)
+			result, _ := json.Marshal(entity.HttpResult{Status: false, Code: "1001", Message: "请求头 Authorization 校验失败。"})
+			_, _ = fmt.Fprintf(w, string(result))
+			return
+		}
+
+		// 如果通过校验,继续处理下一个处理程序
+		next.ServeHTTP(w, r)
+	})
+}

+ 51 - 0
compile-service/package/util/util_exec.go

@@ -0,0 +1,51 @@
+package util
+
+import (
+	"cicv-data-closedloop/compile-service/package/config/c_log"
+	"os/exec"
+)
+
+func Execute(name string, arg ...string) (*exec.Cmd, string, error) {
+	cmd := exec.Command(name, arg...)
+	combinedOutput, err := cmd.CombinedOutput()
+	if err != nil {
+		c_log.GlobalLogger.Info("命令", name, " ", arg, "执行出错:", err)
+		return nil, string(combinedOutput), err
+	}
+	return cmd, string(combinedOutput), nil
+}
+
+func ExecuteWithDir(dir string, name string, arg ...string) (*exec.Cmd, string, error) {
+	cmd := exec.Command(name, arg...)
+	cmd.Dir = dir
+	combinedOutput, err := cmd.CombinedOutput()
+	if err != nil {
+		return nil, "", err
+	}
+	return cmd, string(combinedOutput), nil
+}
+
+func ExecuteWithEnvAndDir(envs []string, dir string, name string, arg ...string) (*exec.Cmd, string, error) {
+	cmd := exec.Command(name, arg...)
+	cmd.Dir = dir
+	for _, env := range envs {
+		cmd.Env = append(cmd.Env, env)
+	}
+	combinedOutput, err := cmd.CombinedOutput()
+	if err != nil {
+		return nil, string(combinedOutput), err
+	}
+	return cmd, string(combinedOutput), nil
+}
+
+//func ExecuteWithEnvAndDir(envs []string, dir string, name string, arg ...string) (*exec.Cmd, error) {
+//	cmd := exec.Command(name, arg...)
+//	cmd.Dir = dir
+//	for _, env := range envs {
+//		cmd.Env = append(cmd.Env, env)
+//	}
+//	if err := cmd.Run(); err != nil {
+//		return nil, err
+//	}
+//	return cmd, nil
+//}

+ 42 - 0
compile-service/package/util/util_io.go

@@ -0,0 +1,42 @@
+package util
+
+import (
+	"os"
+	"path/filepath"
+)
+
+// RemoveDir 递归删除目录及其下的所有文件和子目录
+func RemoveDir(dirPath string) error {
+	// 打开目录
+	dir, err := os.Open(dirPath)
+	if err != nil {
+		return err
+	}
+	defer dir.Close()
+	// 读取目录下的文件和子目录
+	fileInfos, err := dir.Readdir(-1)
+	if err != nil {
+		return err
+	}
+	// 遍历文件和子目录
+	for _, fileInfo := range fileInfos {
+		path := filepath.Join(dirPath, fileInfo.Name())
+
+		if fileInfo.IsDir() {
+			// 如果是子目录,递归调用removeDir删除子目录及其下的文件和子目录
+			if err = RemoveDir(path); err != nil {
+				return err
+			}
+		} else {
+			// 如果是文件,直接删除文件
+			if err = os.Remove(path); err != nil {
+				return err
+			}
+		}
+	}
+	// 删除目录本身
+	if err = os.Remove(dirPath); err != nil {
+		return err
+	}
+	return nil
+}

+ 41 - 0
compile-service/package/util/util_oss.go

@@ -0,0 +1,41 @@
+package util
+
+import (
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+	"os"
+	"path/filepath"
+)
+
+// LimitUpload 限速上传
+// limitBit 默认单位为bit/s。比如 41943040 为 5 MB/s。
+func LimitUpload(bucket *oss.Bucket, limitBit int64, ossObjectKey string, localFilePath string) error {
+	fd, err := os.Open(localFilePath)
+	if err != nil {
+		return err
+	}
+	defer fd.Close()
+	if err = bucket.PutObject(ossObjectKey, fd, oss.TrafficLimitHeader(limitBit)); err != nil {
+		return err
+	}
+	return nil
+}
+
+// LimitDownload 限速下载
+// limitBit 默认单位为bit/s。比如 41943040 为 5 MB/s。
+func LimitDownload(bucket *oss.Bucket, limitBit int64, ossObjectKey string, localFilePath string) error {
+	if err := CreateParentDir(localFilePath); err != nil {
+		return err
+	}
+	if err := bucket.GetObjectToFile(ossObjectKey, localFilePath, oss.TrafficLimitHeader(limitBit)); err != nil {
+		return err
+	}
+	return nil
+}
+
+// CreateParentDir 存在不创建,不存在则创建父目录
+func CreateParentDir(filePath string) error {
+	if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
+		return err
+	}
+	return nil
+}

+ 1 - 0
go.mod

@@ -5,6 +5,7 @@ go 1.21
 require (
 	github.com/aliyun/aliyun-oss-go-sdk v3.0.1+incompatible
 	github.com/bluenviron/goroslib/v2 v2.1.4
+	github.com/google/uuid v1.5.0
 	github.com/sirupsen/logrus v1.9.3
 	gopkg.in/yaml.v2 v2.4.0
 	gopkg.in/yaml.v3 v3.0.1

+ 2 - 0
go.sum

@@ -5,6 +5,8 @@ github.com/bluenviron/goroslib/v2 v2.1.4/go.mod h1:2ktaeJd8Jlim7R5Uv4n0kWoozdaCu
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
 github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
 github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=

+ 49 - 0
plugin-vaildate/main/main.go

@@ -0,0 +1,49 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"plugin"
+)
+
+func main() {
+
+	soPath := os.Args[1]
+	//soPath := "/home/cicv/GolandProjects/rosbag-handle/plugin/brakefault.so"
+
+	open, err := plugin.Open(soPath)
+	if err != nil {
+		fmt.Print("1")
+		return
+	}
+	// 校验 Topic 函数
+	topic, err := open.Lookup("Topic")
+	if err != nil {
+		fmt.Print("2")
+		return
+	}
+	_, ok := topic.(func() string)
+	if ok != true {
+		fmt.Print("3")
+		return
+	}
+	// 校验 Label 函数
+	label, err := open.Lookup("Label")
+	if err != nil {
+		fmt.Print("4")
+		return
+	}
+	_, ok = label.(func() string)
+	if ok != true {
+		fmt.Print("5")
+		return
+	}
+	// 校验 Rule 函数
+	_, err = open.Lookup("Label")
+	if err != nil {
+		fmt.Print("6")
+		return
+	}
+
+	fmt.Print("0")
+}