This commit is contained in:
weizhihong 2023-10-20 15:54:31 +08:00
commit a7b7caa9d1
11 changed files with 162 additions and 51 deletions

View File

@ -20,6 +20,7 @@ import (
apiproto "joylink.club/bj-rtsts-server/grpcproto" apiproto "joylink.club/bj-rtsts-server/grpcproto"
"joylink.club/bj-rtsts-server/middleware" "joylink.club/bj-rtsts-server/middleware"
"joylink.club/bj-rtsts-server/service" "joylink.club/bj-rtsts-server/service"
"joylink.club/bj-rtsts-server/sys_error"
) )
func InitSimulationRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { func InitSimulationRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
@ -78,18 +79,24 @@ func initPublishMapInfo() {
func createByProjectId(c *gin.Context) { func createByProjectId(c *gin.Context) {
req := dto.SimulationCreateReqDto{} req := dto.SimulationCreateReqDto{}
if err := c.ShouldBind(&req); nil != err { if err := c.ShouldBind(&req); nil != err {
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()}) // panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
panic(sys_error.New("测试启动失败,请求参数异常", err))
} }
mapInfos := service.QueryProjectPublishedGi(req.ProjectId) mapInfos := service.QueryProjectPublishedGi(req.ProjectId)
if len(mapInfos) == 0 { if len(mapInfos) == 0 {
panic(dto.ErrorDto{Code: dto.DataNotExist, Message: "项目未关联地图"}) // panic(dto.ErrorDto{Code: dto.DataNotExist, Message: "项目未关联地图"})
panic(sys_error.New("测试启动失败,项目未关联发布图"))
} }
mapIds := make([]int32, len(mapInfos)) mapIds := make([]int32, len(mapInfos))
for i, mapInfo := range mapInfos { for i, mapInfo := range mapInfos {
mapIds[i] = mapInfo.ID mapIds[i] = mapInfo.ID
} }
rsp := dto.SimulationCreateRspDto{ProjectId: req.ProjectId, MapId: mapIds[0], MapIds: mapIds} rsp := dto.SimulationCreateRspDto{ProjectId: req.ProjectId, MapId: mapIds[0], MapIds: mapIds}
rsp.SimulationId = simulation.CreateSimulation(req.ProjectId, mapIds) simulationId, err := simulation.CreateSimulation(req.ProjectId, mapIds)
if err != nil {
panic(sys_error.New("测试启动失败", err))
}
rsp.SimulationId = simulationId
c.JSON(http.StatusOK, &rsp) c.JSON(http.StatusOK, &rsp)
} }

View File

@ -10,6 +10,7 @@ import (
"joylink.club/bj-rtsts-server/dto" "joylink.club/bj-rtsts-server/dto"
"joylink.club/bj-rtsts-server/middleware" "joylink.club/bj-rtsts-server/middleware"
"joylink.club/bj-rtsts-server/service" "joylink.club/bj-rtsts-server/service"
"joylink.club/bj-rtsts-server/sys_error"
) )
func InitUserRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { func InitUserRouter(api *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
@ -37,9 +38,12 @@ func register(c *gin.Context) {
user := &dto.RegisterUser{} user := &dto.RegisterUser{}
err := c.BindJSON(user) err := c.BindJSON(user)
if err != nil { if err != nil {
slog.Warn("用户注册失败", err) panic(sys_error.New("注册失败,参数错误", err))
}
err = service.Register(user)
if err != nil {
panic(sys_error.New("注册失败", err))
} }
service.Register(user)
c.JSON(http.StatusOK, "ok") c.JSON(http.StatusOK, "ok")
} }

View File

@ -1,12 +1,12 @@
package simulation package simulation
import ( import (
"fmt"
"strconv" "strconv"
"sync" "sync"
"joylink.club/bj-rtsts-server/ats/verify/simulation/wayside/memory" "joylink.club/bj-rtsts-server/ats/verify/simulation/wayside/memory"
"joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/config"
"joylink.club/bj-rtsts-server/sys_error"
"joylink.club/bj-rtsts-server/third_party/dynamics" "joylink.club/bj-rtsts-server/third_party/dynamics"
"joylink.club/bj-rtsts-server/third_party/semi_physical_train" "joylink.club/bj-rtsts-server/third_party/semi_physical_train"
@ -27,16 +27,18 @@ func IsExistSimulation() bool {
} }
// 创建仿真对象 // 创建仿真对象
func CreateSimulation(projectId int32, mapIds []int32) string { func CreateSimulation(projectId int32, mapIds []int32) (string, error) {
simulationId := createSimulationId(projectId) simulationId := createSimulationId(projectId)
_, e := simulationMap.Load(simulationId) _, e := simulationMap.Load(simulationId)
if !e && IsExistSimulation() { if !e && IsExistSimulation() {
panic(dto.ErrorDto{Code: dto.DataAlreadyExist, Message: "已有仿真在运行"}) // panic(dto.ErrorDto{Code: dto.DataAlreadyExist, Message: "已有仿真在运行"})
return "", sys_error.New("一套环境同时只能运行一个仿真")
} }
if !e { if !e {
verifySimulation, err := memory.CreateSimulation(projectId, mapIds) verifySimulation, err := memory.CreateSimulation(projectId, mapIds)
if err != nil { if err != nil {
panic(fmt.Sprintf("创建仿真失败:%s", err.Error())) return "", err
// panic(fmt.Sprintf("创建仿真失败:%s", err.Error()))
} }
verifySimulation.SimulationId = simulationId verifySimulation.SimulationId = simulationId
if config.Config.Dynamics.Open { if config.Config.Dynamics.Open {
@ -44,7 +46,8 @@ func CreateSimulation(projectId int32, mapIds []int32) string {
lineBaseInfo := verifySimulation.BuildLineBaseInfo() lineBaseInfo := verifySimulation.BuildLineBaseInfo()
err := dynamics.Default().RequestStartSimulation(lineBaseInfo) err := dynamics.Default().RequestStartSimulation(lineBaseInfo)
if err != nil { if err != nil {
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()}) return "", err
// panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()})
} }
dynamics.Default().Start(verifySimulation) dynamics.Default().Start(verifySimulation)
} }
@ -53,8 +56,10 @@ func CreateSimulation(projectId int32, mapIds []int32) string {
semi_physical_train.Default().Start(verifySimulation) semi_physical_train.Default().Start(verifySimulation)
} }
simulationMap.Store(simulationId, verifySimulation) simulationMap.Store(simulationId, verifySimulation)
// 全部成功,启动仿真
verifySimulation.World.StartUp()
} }
return simulationId return simulationId, nil
} }
// 删除仿真对象 // 删除仿真对象

View File

@ -178,9 +178,12 @@ func turnoutMapToEcsLink(repo *repository.Repository, id string, port string, of
default: default:
panic(dto.ErrorDto{Code: dto.DataNotExist, Message: fmt.Sprintf("无效端口【%s】偏移量", port)}) panic(dto.ErrorDto{Code: dto.DataNotExist, Message: fmt.Sprintf("无效端口【%s】偏移量", port)})
} }
// 统一坐标 岔心公里标 // 岔心公里标
crossKm = convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_None)) crossKm = turnout.GetTurnoutKm(proto2.Port_None)
portKm = convertRepoBaseKm(repo, portKm) portKm, err := repo.ConvertKilometer(portKm, crossKm.CoordinateSystem)
if err != nil {
panic(err)
}
// 关联link // 关联link
link := portPosition.Link() link := portPosition.Link()
isStart := link.ARelation().Device().Id() == id isStart := link.ARelation().Device().Id() == id
@ -247,9 +250,11 @@ func ecsLinkMapToTurnout(repo *repository.Repository, isA bool, offset int64, up
} }
deviceId = tp.Turnout().Id() deviceId = tp.Turnout().Id()
tpOffset := tp.Turnout().FindLinkPositionByPort(tp.Port()).Offset() tpOffset := tp.Turnout().FindLinkPositionByPort(tp.Port()).Offset()
// 统一坐标 crossKmInfo, portKmInfo := tp.Turnout().GetTurnoutKm(proto2.Port_None), tp.Turnout().GetTurnoutKm(tp.Port())
crossKmInfo := convertRepoBaseKm(repo, tp.Turnout().GetTurnoutKm(proto2.Port_None)) portKmInfo, err := repo.ConvertKilometer(portKmInfo, crossKmInfo.CoordinateSystem)
portKmInfo := convertRepoBaseKm(repo, tp.Turnout().GetTurnoutKm(tp.Port())) if err != nil {
panic(err)
}
crossKm, portKm := crossKmInfo.Value, portKmInfo.Value crossKm, portKm := crossKmInfo.Value, portKmInfo.Value
if isA { if isA {
deviceOffset = tpOffset - (tpOffset - offset) deviceOffset = tpOffset - (tpOffset - offset)
@ -344,5 +349,9 @@ func concertTrainKilometer(kilometer, offset int64, tendTo bool) int64 {
// 转换成统一坐标公里标 // 转换成统一坐标公里标
func convertRepoBaseKm(r *repository.Repository, km *proto2.Kilometer) *proto2.Kilometer { func convertRepoBaseKm(r *repository.Repository, km *proto2.Kilometer) *proto2.Kilometer {
return r.ConvertKilometer(km, r.GetCoordinateInfo().Coordinate) k, err := r.ConvertKilometer(km, r.GetCoordinateInfo().Coordinate)
if err != nil {
panic(err)
}
return k
} }

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"math" "math"
"runtime"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -18,6 +19,7 @@ import (
"joylink.club/bj-rtsts-server/ats/verify/protos/graphicData" "joylink.club/bj-rtsts-server/ats/verify/protos/graphicData"
"joylink.club/bj-rtsts-server/ats/verify/protos/state" "joylink.club/bj-rtsts-server/ats/verify/protos/state"
"joylink.club/bj-rtsts-server/dto" "joylink.club/bj-rtsts-server/dto"
"joylink.club/bj-rtsts-server/sys_error"
"joylink.club/bj-rtsts-server/third_party/dynamics" "joylink.club/bj-rtsts-server/third_party/dynamics"
"joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/message"
"joylink.club/bj-rtsts-server/third_party/semi_physical_train" "joylink.club/bj-rtsts-server/third_party/semi_physical_train"
@ -104,21 +106,22 @@ func CreateSimulation(projectId int32, mapIds []int32) (*VerifySimulation, error
if repo == nil { if repo == nil {
protoRepo, err := buildProtoRepository(mapIds) protoRepo, err := buildProtoRepository(mapIds)
if err != nil { if err != nil {
return nil, err return nil, sys_error.New("数据错误", err)
} }
protoRepo.Id, protoRepo.Version = repoId, repoVersion protoRepo.Id, protoRepo.Version = repoId, repoVersion
newRepo, err := repository.BuildRepository(protoRepo) newRepo, err := repository.BuildRepository(protoRepo)
if err != nil { if err != nil {
return nil, err return nil, sys_error.New("数据错误", err)
} }
repo = newRepo repo = newRepo
} }
// 构建所有UID映射关系 // 构建所有UID映射关系
allUidMap := buildRepositoryAllUidsMap(mapIds, repo) allUidMap := buildRepositoryAllUidsMap(mapIds, repo)
//创建仿真 //创建仿真
// worldId := world.CreateSimulation(repo) w, err := rtss_simulation.NewSimulation(repo)
w := rtss_simulation.NewSimulation(repo) if err != nil {
w.StartUp() return nil, sys_error.New("仿真创建失败", err)
}
verifySimulation := &VerifySimulation{ verifySimulation := &VerifySimulation{
MapIds: mapIds, MapIds: mapIds,
ProjectId: projectId, ProjectId: projectId,
@ -128,6 +131,11 @@ func CreateSimulation(projectId int32, mapIds []int32) (*VerifySimulation, error
WorldId: w.Id(), WorldId: w.Id(),
uidMap: allUidMap, uidMap: allUidMap,
} }
// 保证World关闭
runtime.SetFinalizer(verifySimulation, func(verifySimulation *VerifySimulation) {
slog.Info("---关闭仿真World---")
verifySimulation.World.Close()
})
return verifySimulation, nil return verifySimulation, nil
} }

View File

@ -11,7 +11,7 @@ dynamics:
udpRemotePort: 3000 udpRemotePort: 3000
udpRemoteTrainPort: 3001 udpRemoteTrainPort: 3001
httpPort: 7800 httpPort: 7800
open: true open: false
# VOBC # VOBC
vobc: vobc:
ip: 10.60.1.59 ip: 10.60.1.59

39
init.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"log/slog" "log/slog"
"net" "net"
"net/http" "net/http"
@ -13,6 +14,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"joylink.club/bj-rtsts-server/db/dbquery" "joylink.club/bj-rtsts-server/db/dbquery"
"joylink.club/bj-rtsts-server/dto" "joylink.club/bj-rtsts-server/dto"
"joylink.club/bj-rtsts-server/sys_error"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -45,19 +47,32 @@ func InitServer() *gin.Engine {
engine.Use(cors.New(conf)) engine.Use(cors.New(conf))
// gin panic 异常处理,默认处理为 // gin panic 异常处理,默认处理为
engine.Use(CustomRecoveryWithSlog(slog.Default(), true, func(c *gin.Context, e interface{}) { engine.Use(CustomRecoveryWithSlog(slog.Default(), true, func(c *gin.Context, e interface{}) {
switch e := e.(type) { be, ok := e.(*sys_error.BusinessError)
case error: if !ok {
c.JSON(http.StatusInternalServerError, &dto.ErrorDto{ e, ok := e.(error)
Code: dto.LogicError, if ok {
Tip: dto.ErrorTipMap[dto.LogicError], be = sys_error.New("未知错误", e)
Message: e.Error(), } else {
}) be = sys_error.New("未知错误", fmt.Errorf("%v", e))
case dto.ErrorDto: }
e.Tip = dto.ErrorTipMap[e.Code]
c.JSON(http.StatusInternalServerError, e)
default:
c.JSON(http.StatusInternalServerError, e)
} }
c.JSON(http.StatusInternalServerError, &dto.ErrorDto{
Tip: be.UserMsg,
Message: be.Error(),
})
// switch e := e.(type) {
// case error:
// c.JSON(http.StatusInternalServerError, &dto.ErrorDto{
// Code: dto.LogicError,
// Tip: dto.ErrorTipMap[dto.LogicError],
// Message: e.Error(),
// })
// case dto.ErrorDto:
// e.Tip = dto.ErrorTipMap[e.Code]
// c.JSON(http.StatusInternalServerError, e)
// default:
// c.JSON(http.StatusInternalServerError, e)
// }
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
c.Abort() c.Abort()
})) }))

View File

@ -9,6 +9,7 @@ import (
"joylink.club/bj-rtsts-server/db/model" "joylink.club/bj-rtsts-server/db/model"
"joylink.club/bj-rtsts-server/dto" "joylink.club/bj-rtsts-server/dto"
"joylink.club/bj-rtsts-server/service" "joylink.club/bj-rtsts-server/service"
"joylink.club/bj-rtsts-server/sys_error"
) )
// 用户权限缓存 // 用户权限缓存
@ -40,7 +41,7 @@ func permissionMiddleware() gin.HandlerFunc {
return return
} }
slog.Error("无权限操作请求路径", "path", path, "method", method) slog.Error("无权限操作请求路径", "path", path, "method", method)
panic(dto.ErrorDto{Code: dto.NoAuthOperationError, Message: "无权限操作"}) panic(sys_error.New("权限不足"))
} }
} }

View File

@ -2,13 +2,13 @@ package service
import ( import (
"fmt" "fmt"
"log/slog"
"sort" "sort"
"time" "time"
"joylink.club/bj-rtsts-server/db/dbquery" "joylink.club/bj-rtsts-server/db/dbquery"
"joylink.club/bj-rtsts-server/db/model" "joylink.club/bj-rtsts-server/db/model"
"joylink.club/bj-rtsts-server/dto" "joylink.club/bj-rtsts-server/dto"
"joylink.club/bj-rtsts-server/sys_error"
) )
// 分页查询用户列表 // 分页查询用户列表
@ -28,23 +28,23 @@ func PagingQueryUser(query *dto.PageUserReqDto) (*dto.PageDto, error) {
return &dto.PageDto{Total: int(total), PageQueryDto: query.PageQueryDto, Records: linkUserRole(records)}, err return &dto.PageDto{Total: int(total), PageQueryDto: query.PageQueryDto, Records: linkUserRole(records)}, err
} }
func Register(user *dto.RegisterUser) { func Register(user *dto.RegisterUser) error {
defer func() {
err := recover()
if err != nil {
slog.Warn("用户注册失败", err)
panic(err)
}
}()
u := dbquery.User u := dbquery.User
uq := u.Where() uq := u.Where()
uq = uq.Where(u.Mobile.Eq(user.Mobile)) uq = uq.Where(u.Mobile.Eq(user.Mobile))
findCounter, _ := uq.Count() findCounter, err := uq.Count()
if err != nil {
return sys_error.New("数据服务异常", err)
}
if findCounter > 0 { if findCounter > 0 {
panic(dto.ErrorDto{Code: dto.DataAlreadyExist, Message: "重复的手机号"}) return sys_error.New("手机号已存在")
} }
user.RegisterTime = time.Now() user.RegisterTime = time.Now()
u.Save(user) err = u.Save(user)
if err != nil {
return sys_error.New("数据服务异常", err)
}
return nil
} }
func FindUserInfo(userId int32) *dto.UserRspDto { func FindUserInfo(userId int32) *dto.UserRspDto {

61
sys_error/error.go Normal file
View File

@ -0,0 +1,61 @@
package sys_error
import (
"fmt"
"strings"
)
// 业务错误定义
type BusinessError struct {
// 用户提示信息
UserMsg string
// 错误信息传递(用于开发回溯定位,不给用户展示)
Errors []string
}
// 新建业务错误
// 如果errs为空,则返回一个只包含用户提示信息的业务错误
// 如果errs不为空,如果errs是一个业务错误,则附加错误信息,否则返回一个包含用户提示信息和错误信息的业务错误
func New(userMsg string, errs ...error) *BusinessError {
if len(errs) == 1 {
be, ok := errs[0].(*BusinessError)
if ok {
be.prependUserMsg(userMsg)
return be
} else {
return &BusinessError{
UserMsg: userMsg,
Errors: []string{errs[0].Error()},
}
}
}
return &BusinessError{
UserMsg: userMsg,
// Errors: convert(errs),
}
}
func IsBusinessError(err error) bool {
_, ok := err.(*BusinessError)
return ok
}
func (e *BusinessError) prependUserMsg(userMsg string) {
e.UserMsg = fmt.Sprintf("%s,%s", userMsg, e.UserMsg)
}
// func convert(err []error) []string {
// s := []string{}
// for _, e := range err {
// s = append(s, e.Error())
// }
// return s
// }
func (e *BusinessError) Append(err error) {
e.Errors = append(e.Errors, err.Error())
}
func (e *BusinessError) Error() string {
return strings.Join(e.Errors, ", ")
}

View File

@ -11,6 +11,7 @@ import (
"time" "time"
"joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/config"
"joylink.club/bj-rtsts-server/sys_error"
"joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/message"
"joylink.club/bj-rtsts-server/third_party/udp" "joylink.club/bj-rtsts-server/third_party/udp"
) )
@ -117,14 +118,14 @@ func (d *dynamics) RequestStartSimulation(base *message.LineBaseInfo) error {
data, _ := json.Marshal(base) data, _ := json.Marshal(base)
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data)) resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
if err != nil { if err != nil {
return fmt.Errorf("请求启动仿真异常: %v", err) return sys_error.New("动力学开始仿真请求发送错误", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
var buf []byte var buf []byte
_, err = resp.Body.Read(buf) _, err = resp.Body.Read(buf)
if err != nil { if err != nil {
return fmt.Errorf("请求启动仿真读取相应异常: %v", err) return sys_error.New("动力学开始仿真请求响应错误", err)
} }
return nil return nil
} }