383 lines
12 KiB
Go
383 lines
12 KiB
Go
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(sys_error.New(fmt.Sprintf("[mapId:%d]类型映射错误 :%v", mapId, value), err))
|
||
}
|
||
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
|
||
}
|