rts-sim-testing-service/ts/simulation/wayside/memory/wayside_memory_map.go
2023-10-26 17:16:07 +08:00

383 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package memory
import (
"fmt"
"math"
"sort"
"strconv"
"strings"
"sync"
"joylink.club/rtsssimulation/repository"
proto2 "joylink.club/rtsssimulation/repository/model/proto"
"google.golang.org/protobuf/proto"
"joylink.club/bj-rtsts-server/db/dbquery"
"joylink.club/bj-rtsts-server/db/model"
"joylink.club/bj-rtsts-server/dto"
"joylink.club/bj-rtsts-server/sys_error"
"joylink.club/bj-rtsts-server/ts/protos/graphicData"
"joylink.club/bj-rtsts-server/ts/protos/state"
)
var (
giTypeMap sync.Map
giNameMap sync.Map
giDataMap sync.Map
)
// 将发布的地图数据放入内存中
func PublishMapVerifyStructure(graphic *model.PublishedGi) {
giTypeMap.Store(graphic.ID, graphicData.PictureType(graphic.Type))
giNameMap.Store(graphic.Name, graphic.ID)
var message proto.Message
switch graphicData.PictureType(graphic.Type) {
case graphicData.PictureType_StationLayout:
message = &graphicData.RtssGraphicStorage{}
case graphicData.PictureType_RelayCabinetLayout:
message = &graphicData.RelayCabinetGraphicStorage{}
case graphicData.PictureType_Psl:
message = &graphicData.PslGraphicStorage{}
case graphicData.PictureType_IBP:
message = &graphicData.IBPGraphicStorage{}
}
err := proto.Unmarshal(graphic.Proto, message)
if err != nil {
panic(&dto.ErrorDto{Code: dto.LogicError, Message: fmt.Sprintf("[id:%d]proto数据反序列化失败:%s", graphic.ID, err)})
}
giDataMap.Store(graphic.ID, message)
// 初始化地图结构
switch graphicData.PictureType(graphic.Type) {
case graphicData.PictureType_StationLayout:
graphicStorage := message.(*graphicData.RtssGraphicStorage)
giUidMap.Store(graphic.ID, initStationUid(graphicStorage))
case graphicData.PictureType_RelayCabinetLayout:
graphicStorage := message.(*graphicData.RelayCabinetGraphicStorage)
giUidMap.Store(graphic.ID, initRelayCabinetUid(graphicStorage))
}
}
func GenerateElementUid(city, lineId string, stationIndexList []string, code string) string {
sort.Strings(stationIndexList)
var idArr []string
idArr = append(idArr, city, lineId)
idArr = append(idArr, stationIndexList...)
idArr = append(idArr, code)
return strings.Join(idArr, "_")
}
// 移除内存中的地图信息
func DeleteMapVerifyStructure(mapId int32) {
giTypeMap.Delete(mapId)
giDataMap.Delete(mapId)
giUidMap.Delete(mapId)
}
func QueryGiType(mapId int32) graphicData.PictureType {
value, ok := giTypeMap.Load(mapId)
if !ok {
graphic, err := dbquery.PublishedGi.Debug().Where(dbquery.PublishedGi.ID.Eq(mapId)).First() // 当缓存缺失新地图时,查询一次
if err != nil {
panic(&dto.ErrorDto{Code: dto.LogicError, Message: fmt.Sprintf("[mapId:%d]类型映射错误 :%v", mapId, value)})
}
PublishMapVerifyStructure(graphic)
return graphicData.PictureType(graphic.Type)
}
return value.(graphicData.PictureType)
}
func QueryGiData[T proto.Message](mapId int32) T {
value, _ := giDataMap.Load(mapId)
return value.(T)
}
func QueryGiId(name string) int32 {
value, _ := giNameMap.Load(name)
return value.(int32)
}
// 根据区段道岔偏移量返回linkID和link相对偏移量
func QueryEcsLinkByDeviceInfo(repo *repository.Repository, mapId int32, status *state.TrainState) (int32, int64, bool, bool, int64) {
id := status.HeadDeviceId
if status.DevicePort == "" {
uid := QueryUidByMidAndComId(mapId, id, &graphicData.Section{})
return sectionMapToEcsLink(repo, uid, status)
} else {
uid := QueryUidByMidAndComId(mapId, id, &graphicData.Turnout{})
return turnoutMapToEcsLink(repo, uid, status)
}
}
func getStorageIBPMapData(mapCode string) *graphicData.IBPGraphicStorage {
// 处理关联的IBP盘信息
if mapCode == "" {
return nil
}
ibpId, ok := giNameMap.Load(mapCode)
if !ok {
return nil
}
ibpMapData, ok := giDataMap.Load(ibpId)
if !ok {
return nil
}
return ibpMapData.(*graphicData.IBPGraphicStorage)
}
// 根据物理区段上的偏移量基于区段A端找到所在link的linkId与偏移量
func sectionMapToEcsLink(repo *repository.Repository, id string, status *state.TrainState) (int32, int64, bool, bool, int64) {
runDirection := status.RunDirection
section := repo.FindPhysicalSection(id)
if section == nil {
panic(sys_error.New(fmt.Sprintf("地图不存在uid:%s缓存", id)))
}
ao, bo := section.ALinkPosition().Offset(), section.BLinkPosition().Offset()
// 检查区段长度是否足够放下列车
status.HeadOffset = checkDeviceContainTrain(status.HeadOffset, status.TrainLength, bo-ao, id)
link := section.ALinkPosition().Link()
// 是否从A到B统一坐标
ak, bk := convertRepoBaseKm(repo, section.AKilometer()), convertRepoBaseKm(repo, section.BKilometer())
akv, bkv := ak.Value, bk.Value
// 上行
var up, abDirection bool
if runDirection {
abDirection = akv < bkv
if abDirection {
up = ao < bo
} else {
up = ao > bo
}
} else {
abDirection = akv > bkv
if abDirection {
up = ao < bo
} else {
up = ao > bo
}
}
linkId, _ := strconv.Atoi(link.Identity.Id())
trainKilometer := concertTrainKilometer(akv, status.HeadOffset, up)
if ao < bo {
return int32(linkId), ao + status.HeadOffset, up, abDirection, trainKilometer
} else {
return int32(linkId), ao - status.HeadOffset, up, abDirection, trainKilometer
}
}
// 根据道岔上的偏移量基于岔心位置找到所在link的linkId与偏移量
func turnoutMapToEcsLink(repo *repository.Repository, id string, status *state.TrainState) (int32, int64, bool, bool, int64) {
port, runDirection := status.DevicePort, status.RunDirection
turnout := repo.FindTurnout(id)
if turnout == nil {
panic(sys_error.New(fmt.Sprintf("不存在道岔【uid:%s】", id)))
}
var portPosition *repository.LinkPosition
var crossKm, portKm *proto2.Kilometer
switch port {
case "A":
portPosition = turnout.FindLinkPositionByPort(proto2.Port_A)
portKm = turnout.GetTurnoutKm(proto2.Port_A)
case "B":
portPosition = turnout.FindLinkPositionByPort(proto2.Port_B)
portKm = turnout.GetTurnoutKm(proto2.Port_B)
case "C":
portPosition = turnout.FindLinkPositionByPort(proto2.Port_C)
portKm = turnout.GetTurnoutKm(proto2.Port_C)
default:
panic(sys_error.New(fmt.Sprintf("无效端口【%s】偏移量", port)))
}
// 岔心公里标
crossKm = turnout.GetTurnoutKm(proto2.Port_None)
portKm, err := repo.ConvertKilometer(portKm, crossKm.CoordinateSystem)
if err != nil {
panic(sys_error.New("公里标转换出错", err))
}
// 检查link偏移
status.HeadOffset = checkDeviceContainTrain(status.HeadOffset, status.TrainLength, crossKm.Value-portKm.Value, id)
// 关联link
link := portPosition.Link()
isStart := link.ARelation().Device().Id() == id
up := runDirection
if (portKm.Value > crossKm.Value) != isStart {
up = !runDirection
}
pointTo := (portKm.Value > crossKm.Value) == runDirection
trainKilometer := concertTrainKilometer(crossKm.Value, status.HeadOffset, pointTo)
linkId, _ := strconv.Atoi(link.Identity.Id())
if isStart {
return int32(linkId), status.HeadOffset, up, pointTo, trainKilometer
} else {
// 道岔长度
turnoutLen := int64(math.Abs(float64(portKm.Value - crossKm.Value)))
return int32(linkId), portPosition.Offset() + turnoutLen - status.HeadOffset, up, pointTo, trainKilometer
}
}
// 根据linkID和link相对偏移量返回区段道岔偏移量
// 设备ID、端口、偏移量、上下行、AB走向
func QueryDeviceByCalcLink(repo *repository.Repository, id string, offset int64, up bool) (
deviceId, port string, deviceOffset int64, runDirection, pointTo bool, km int64) {
link := repo.FindLink(id)
if link == nil {
panic(dto.ErrorDto{Code: dto.DataNotExist, Message: fmt.Sprintf("未找到link【%s】", id)})
}
if offset > link.Length() {
panic(dto.ErrorDto{Code: dto.DataNotExist, Message: fmt.Sprintf("偏移【%d】超出link范围【%d】", offset, link.Length())})
}
// 判断是否在道岔上
onTurnout, isA := isOnLinkTurnout(link, offset)
if onTurnout {
return ecsLinkMapToTurnout(repo, isA, offset, up, link)
} else {
return ecsLinkMapToSection(repo, offset, up, link)
}
}
// 是否在link的道岔上
func isOnLinkTurnout(link *repository.Link, offset int64) (bool, bool) {
aTp := link.ARelation()
var turnoutOffset int64
if aTp != nil {
turnoutOffset = aTp.Turnout().FindLinkPositionByPort(aTp.Port()).Offset()
}
if offset <= turnoutOffset {
return true, true
}
bTp := link.BRelation()
if bTp != nil {
turnoutOffset = bTp.Turnout().FindLinkPositionByPort(bTp.Port()).Offset()
return offset >= turnoutOffset, false
}
return false, false
}
// 处理在道岔上link映射
func ecsLinkMapToTurnout(repo *repository.Repository, isA bool, offset int64, up bool, link *repository.Link) (
deviceId, port string, deviceOffset int64, runDirection, pointTo bool, km int64) {
tp := link.ARelation()
if !isA {
tp = link.BRelation()
}
deviceId = tp.Turnout().Id()
tpOffset := tp.Turnout().FindLinkPositionByPort(tp.Port()).Offset()
crossKmInfo, portKmInfo := tp.Turnout().GetTurnoutKm(proto2.Port_None), tp.Turnout().GetTurnoutKm(tp.Port())
portKmInfo, err := repo.ConvertKilometer(portKmInfo, crossKmInfo.CoordinateSystem)
if err != nil {
panic(err)
}
crossKm, portKm := crossKmInfo.Value, portKmInfo.Value
if isA {
deviceOffset = tpOffset - (tpOffset - offset)
pointTo = !up
} else {
deviceOffset = link.Length() - offset
pointTo = up
}
// 查询公里标大于端口公里标
if crossKm > portKm {
km = crossKm - deviceOffset
} else {
km = crossKm + deviceOffset
}
if up && isA { // link offset 趋大A端岔心 ---> 边界
runDirection = crossKm < portKm
} else if up && !isA { // link offset 趋大B端边界 ---> 岔心
runDirection = crossKm > portKm
} else if !up && isA { // link offset 趋小A端边界 ---> 岔心
runDirection = crossKm > portKm
} else { // link offset 趋小B端岔心 ---> 边界
runDirection = crossKm < portKm
}
switch tp.Port() {
case proto2.Port_A:
port = "A"
case proto2.Port_B:
port = "B"
case proto2.Port_C:
port = "C"
}
return
}
// 处理在道岔上link映射
func ecsLinkMapToSection(repo *repository.Repository, offset int64, up bool, link *repository.Link) (
deviceId, port string, deviceOffset int64, runDirection, pointTo bool, km int64) {
var section *repository.PhysicalSection
for _, s := range link.PhysicalSections() {
ao, bo := s.ALinkPosition().Offset(), s.BLinkPosition().Offset()
if (ao < offset && offset <= bo) || (bo < offset && offset <= ao) {
section = s
break
}
}
if section == nil {
panic(dto.ErrorDto{Code: dto.DataNotExist, Message: fmt.Sprintf("未找到link设备偏移【%d】", offset)})
}
// 元素ID
deviceId = section.Id()
// link偏移变大方向
ao, bo := section.ALinkPosition().Offset(), section.BLinkPosition().Offset()
ak, bk := convertRepoBaseKm(repo, section.AKilometer()), convertRepoBaseKm(repo, section.BKilometer())
akv, bkv := ak.Value, bk.Value
if up {
if ao < bo {
runDirection = akv < bkv
} else {
runDirection = akv > bkv
}
} else {
if ao > bo {
runDirection = akv < bkv
} else {
runDirection = akv > bkv
}
}
pointTo = runDirection == (akv < bkv)
// a点偏移 大于 b点偏移
if ao > bo {
deviceOffset = ao - offset
} else {
deviceOffset = offset - ao
}
// a点公里标 大于 b点公里标
if akv > bkv {
km = akv - deviceOffset
} else {
km = akv + deviceOffset
}
return
}
// 查找列车的公里标
func concertTrainKilometer(kilometer, offset int64, tendTo bool) int64 {
if tendTo {
return kilometer - offset
} else {
return kilometer + offset
}
}
// 转换成统一坐标公里标
func convertRepoBaseKm(r *repository.Repository, km *proto2.Kilometer) *proto2.Kilometer {
k, err := r.ConvertKilometer(km, r.GetCoordinateInfo().Coordinate)
if err != nil {
panic(err)
}
return k
}
// 检查列车是否可以放到设备上
func checkDeviceContainTrain(offset, trainLen, deviceLen int64, deviceName string) int64 {
l := int64(math.Abs(float64(deviceLen)))
if offset > l {
panic(sys_error.New(fmt.Sprintf("偏移【%d】超出【%s】范围【%d】", offset, deviceName, l)))
}
if trainLen > offset { // 如果列车长度超过设置offset
offset = trainLen
}
if offset > l {
panic(sys_error.New(fmt.Sprintf("列车长度【%d】超出【%s】范围【%d】", trainLen, deviceName, l)))
}
return offset
}