rts-sim-testing-service/ts/simulation/wayside/memory/wayside_memory_map.go

602 lines
20 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"
"sort"
"strings"
"sync"
"joylink.club/rtsssimulation/component"
"joylink.club/rtsssimulation/entity"
"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"
)
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)
}
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)
}
// 转换成统一坐标公里标
func convertRepoBaseKm(r *repository.Repository, km *proto2.Kilometer) *proto2.Kilometer {
k, err := r.ConvertKilometer(km, r.GetCoordinateInfo().Coordinate)
if err != nil {
panic(sys_error.New(fmt.Sprintf("公里标转换【%s->%s】错误", km.CoordinateSystem, r.GetCoordinateInfo().Coordinate)))
}
return k
}
// 根据传入link、偏移、link运行方向查找所在设备信息
// 入参仿真、linkId、link偏移量、link运行方向
// 输出linkId、设备Id、设备端口、link偏移量、设备上偏移量、偏移量对应的公里标
func CalcInitializeLink(sim *VerifySimulation, linkId string, offset int64, up bool) (
outLinkId, deviceId, port string, outLinkOffset, deviceOffset int64, km *proto2.Kilometer) {
link := sim.Repo.FindLink(linkId)
if link == nil {
panic(sys_error.New(fmt.Sprintf("未找到link【%s】", linkId)))
}
// 获取计算link与所在偏移
outLinkId, outLinkOffset = findCalcLinkIdAndOffset(sim, link, offset)
calcLink := sim.Repo.FindLink(outLinkId)
// 判断是否在道岔上
onTurnout, isA := isOnLinkTurnout(calcLink, outLinkOffset)
if onTurnout {
deviceId, port, deviceOffset, km = calcTurnoutOffset(sim.Repo, calcLink, isA, outLinkOffset, up)
} else {
deviceId, port, deviceOffset, km = calcSectionOffset(sim.Repo, calcLink, outLinkOffset, up)
}
// 输出当前所在设备、端口、设备偏移
return
}
// 列车是否在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
}
// 查找最终的linkId与link偏移
// 入参仿真、link、link偏移量
// 输出linkId、link偏移量
func findCalcLinkIdAndOffset(sim *VerifySimulation, link *repository.Link, offset int64) (string, int64) {
if 0 <= offset && offset <= link.Length() {
return link.Id(), offset
}
// 超出端、超出长度
tp, length := link.ARelation(), 0-offset
if offset > 0 {
tp, length = link.BRelation(), offset-link.Length()
}
if tp == nil {
panic(sys_error.New("列车偏移超出link位置"))
}
nextPort := getTurnoutNextPort(sim, tp.Device().Id(), tp.Port().String())
if nextPort == proto2.Port_None {
panic(sys_error.New("列车偏移超出link位置"))
}
var nextLink *repository.Link
var isB bool
// 寻找匹配到的link
for _, l := range sim.Repo.LinkList() {
if l.ARelation() != nil && l.ARelation().Port() == nextPort && l.ARelation().Device().Id() == tp.Device().Id() {
nextLink = l
break
}
if l.BRelation() != nil && l.BRelation().Port() == nextPort && l.BRelation().Device().Id() == tp.Device().Id() {
nextLink = l
isB = true
break
}
}
// 没有找到连接信息,说明已经到尽头找不到位置
if nextLink == nil {
panic(sys_error.New(fmt.Sprintf("未找到对应的link信息, linkId=%s, offset=%d", link.Id(), offset)))
}
// 下个link偏移
nextOffset := length
if isB {
nextOffset = nextLink.Length() - length
}
return findCalcLinkIdAndOffset(sim, nextLink, nextOffset)
}
// 根据当前link端寻找下一个link连接端端口
// 入参:仿真、道岔端口
// 输出:道岔端口
func getTurnoutNextPort(sim *VerifySimulation, turnoutId string, port string) proto2.Port {
entry, ok := entity.GetEntityByUid(sim.World, turnoutId)
if !ok {
panic(sys_error.New(fmt.Sprintf("道岔不存在: World id=%d,道岔id=%s", sim.World.Id(), turnoutId)))
}
if !entry.HasComponent(component.TurnoutPositionType) {
panic(sys_error.New(fmt.Sprintf("道岔没有TurnoutPosition组件: World id=%d,道岔id=%s", sim.World.Id(), turnoutId)))
}
// 获取定反数据
pos := component.TurnoutPositionType.Get(entry)
switch port {
case "A":
if pos.Dw {
return proto2.Port_B
} else {
return proto2.Port_C
}
case "B":
if pos.Dw {
return proto2.Port_A
} else {
return proto2.Port_None
}
case "C":
if pos.Dw {
return proto2.Port_None
} else {
return proto2.Port_A
}
}
panic(sys_error.New(fmt.Sprintf("非法端口:端口=%s", port)))
}
// 计算link offset 在道岔上的位置
// 入参仿真Repository、link、是否从A端开始、link偏移量、link运行方向
// 输出设备Id、设备所在端口、设备偏移量、公里标信息地图主坐标系
func calcTurnoutOffset(repo *repository.Repository, link *repository.Link, isA bool, offset int64, up bool) (
deviceId, port string, deviceOffset int64, km *proto2.Kilometer) {
tp := link.ARelation()
if !isA {
tp = link.BRelation()
}
// 设备ID
deviceId = tp.Turnout().Id()
// 端口信息
port = tp.Port().String()
// 设备上的偏移量
if isA {
deviceOffset = offset
} else {
deviceOffset = link.Length() - offset
}
// 公里标计算
crossKmInfo := convertRepoBaseKm(repo, tp.Turnout().GetTurnoutKm(proto2.Port_None))
portKmInfo := convertRepoBaseKm(repo, tp.Turnout().GetTurnoutKm(tp.Port()))
km = &proto2.Kilometer{CoordinateSystem: crossKmInfo.CoordinateSystem}
if crossKmInfo.Value > portKmInfo.Value {
km.Value = crossKmInfo.Value - deviceOffset
} else {
km.Value = crossKmInfo.Value + deviceOffset
}
return
}
// 计算link offset 在区段上的位置
// 入参仿真Repository、link、link偏移量、link运行方向
// 输出设备Id、设备所在端口、设备偏移量、公里标信息地图主坐标系
func calcSectionOffset(repo *repository.Repository, link *repository.Link, offset int64, up bool) (
deviceId, port string, deviceOffset int64, km *proto2.Kilometer) {
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(sys_error.New(fmt.Sprintf("未找到link设备偏移【%d】", offset)))
}
// 元素ID
deviceId = section.Id()
// a点偏移 大于 b点偏移
ao, bo := section.ALinkPosition().Offset(), section.BLinkPosition().Offset()
if ao > bo {
deviceOffset = ao - offset
} else {
deviceOffset = offset - ao
}
// a点公里标 大于 b点公里标
ak, bk := convertRepoBaseKm(repo, section.AKilometer()), convertRepoBaseKm(repo, section.BKilometer())
km = &proto2.Kilometer{CoordinateSystem: ak.CoordinateSystem}
if ak.Value > bk.Value {
km.Value = ak.Value - deviceOffset
} else {
km.Value = ak.Value + deviceOffset
}
return
}
// 根据设备查找link
// 入参设备ID、端口、设备偏移
// 输出linkId、link偏移
func QueryLinkAndOffsetByDevice(repo *repository.Repository, uid, port string, offset int64) (linkId string, linkOffset int64) {
if port == "" {
linkId, linkOffset = sectionToLink(repo, uid, offset)
} else {
linkId, linkOffset = turnoutToLink(repo, uid, port, offset)
}
return
}
// 根据区段设备查找link
// 入参仿真Repository、设备ID、设备偏移
// 输出linkId、link偏移、公里标
func sectionToLink(repo *repository.Repository, uid string, offset int64) (string, int64) {
section := repo.FindPhysicalSection(uid)
if section == nil {
panic(sys_error.New(fmt.Sprintf("地图不存在uid:%s缓存", uid)))
}
ao, bo := section.ALinkPosition().Offset(), section.BLinkPosition().Offset()
if ao < bo {
return section.ALinkPosition().Link().Id(), ao + offset
} else {
return section.ALinkPosition().Link().Id(), ao - offset
}
}
// 根据道岔设备查找link
// 入参仿真Repository、道岔设备ID、道岔端口、设备偏移
// 输出linkId、link偏移
func turnoutToLink(repo *repository.Repository, uid, port string, offset int64) (string, int64) {
turnout := repo.FindTurnout(uid)
if turnout == nil {
panic(sys_error.New(fmt.Sprintf("不存在道岔【uid:%s】", uid)))
}
var portPosition *repository.LinkPosition
switch port {
case "A":
portPosition = turnout.FindLinkPositionByPort(proto2.Port_A)
case "B":
portPosition = turnout.FindLinkPositionByPort(proto2.Port_B)
case "C":
portPosition = turnout.FindLinkPositionByPort(proto2.Port_C)
default:
panic(sys_error.New(fmt.Sprintf("无效端口【%s】偏移量", port)))
}
// 关联link
link := portPosition.Link()
isStart := link.ARelation().Device().Id() == uid
if isStart {
return link.Id(), offset
} else {
return link.Id(), link.Length() - offset
}
}
// 根据设备信息、上下行获取link运行方向ab指向
// 入参仿真Repository、uid、端口、上下行
// 输出link运行方向、设备上的指向
func QueryUpAndABByDevice(repo *repository.Repository, uid, port string, runDirection bool) (up, ab bool) {
if port == "" {
up, ab = runDirectionSectionToUpAndAB(repo, uid, runDirection)
} else {
up, ab = runDirectionTurnoutToUpAndAB(repo, uid, port, runDirection)
}
return
}
// 根据区段及上下行获取link运行方向ab指向
// 入参仿真Repository、区段UID、上下行
// 输出link运行方向、设备上的指向
func runDirectionSectionToUpAndAB(repo *repository.Repository, id string, runDirection bool) (up, ab bool) {
section := repo.FindPhysicalSection(id)
if section == nil {
panic(sys_error.New(fmt.Sprintf("地图不存在uid:%s缓存", id)))
}
ao, bo := section.ALinkPosition().Offset(), section.BLinkPosition().Offset()
// 是否从A到B统一坐标
ak, bk := convertRepoBaseKm(repo, section.AKilometer()), convertRepoBaseKm(repo, section.BKilometer())
ab = (runDirection == (ak.Value <= bk.Value))
up = (ab == (ao < bo))
return
}
// 根据区段及上下行获取link运行方向ab指向
// 入参仿真Repository、道岔UID、道岔端口、上下行
// 输出link运行方向设备上的指向
func runDirectionTurnoutToUpAndAB(repo *repository.Repository, id, port string, runDirection bool) (up, ab bool) {
turnout := repo.FindTurnout(id)
if turnout == nil {
panic(sys_error.New(fmt.Sprintf("不存在道岔【uid:%s】", id)))
}
var portKm *proto2.Kilometer
var portPosition *repository.LinkPosition
switch port {
case "A":
portKm = convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_A))
portPosition = turnout.FindLinkPositionByPort(proto2.Port_A)
case "B":
portKm = convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_B))
portPosition = turnout.FindLinkPositionByPort(proto2.Port_B)
case "C":
portKm = convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_C))
portPosition = turnout.FindLinkPositionByPort(proto2.Port_C)
default:
panic(sys_error.New(fmt.Sprintf("无效端口【%s】偏移量", port)))
}
// 岔心公里标
crossKm := convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_None))
// 是否是link的起始断点
isStart := (portPosition.Link().ARelation() != nil && portPosition.Link().ARelation().Device().Id() == id)
up = runDirection
if (portKm.Value > crossKm.Value) != isStart {
up = !runDirection
}
ab = (portKm.Value > crossKm.Value) == runDirection
return
}
// 根据设备以及link运行方向获取上下行、设备上的运行指向
// 入参仿真Repository、道岔UID、道岔端口、上下行
// 输出link运行方向设备上的指向
func QueryDirectionAndABByDevice(repo *repository.Repository, uid, port string, up bool) (runDirection, ab bool) {
if port == "" {
runDirection, ab = upSectionToDirectionAndAB(repo, uid, up)
} else {
runDirection, ab = upTurnoutToDirectionAndAB(repo, uid, port, up)
}
return
}
// 根据区段以及link运行方向获取上下行、设备上的运行指向
// 入参仿真Repository、道岔UID、道岔端口、上下行
// 输出link运行方向设备上的指向
func upSectionToDirectionAndAB(repo *repository.Repository, uid string, up bool) (runDirection, ab bool) {
section := repo.FindPhysicalSection(uid)
if section == nil {
panic(sys_error.New(fmt.Sprintf("地图不存在uid:%s缓存", uid)))
}
// a点偏移、b点偏移
ao, bo := section.ALinkPosition().Offset(), section.BLinkPosition().Offset()
// a点公里标、b点公里标
ak, bk := convertRepoBaseKm(repo, section.AKilometer()), convertRepoBaseKm(repo, section.BKilometer())
if up {
runDirection = ((ao < bo) == (ak.Value < bk.Value))
} else {
runDirection = ((ao > bo) == (ak.Value < bk.Value))
}
ab = (runDirection == (ak.Value < bk.Value))
return
}
// 根据道岔以及link运行方向获取上下行、设备上的运行指向
// 入参仿真Repository、道岔UID、道岔端口、上下行
// 输出link运行方向设备上的指向
func upTurnoutToDirectionAndAB(repo *repository.Repository, uid, port string, up bool) (runDirection, ab bool) {
turnout := repo.FindTurnout(uid)
if turnout == nil {
panic(sys_error.New(fmt.Sprintf("不存在道岔【uid:%s】", uid)))
}
var port_enum proto2.Port
switch port {
case "A":
port_enum = proto2.Port_A
case "B":
port_enum = proto2.Port_B
case "C":
port_enum = proto2.Port_C
default:
panic(sys_error.New(fmt.Sprintf("无效端口【%s】偏移量", port)))
}
tpo := turnout.FindLinkPositionByPort(port_enum)
isA := tpo.Link().ARelation() != nil && tpo.Link().ARelation().Device().Id() == uid
crossKmInfo := convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_None))
portKmInfo := convertRepoBaseKm(repo, turnout.GetTurnoutKm(port_enum))
if isA {
ab = !up
runDirection = (up == (crossKmInfo.Value < portKmInfo.Value))
} else {
ab = up
runDirection = (up == (crossKmInfo.Value > portKmInfo.Value))
}
return
}
// 根据设备获取列车公里标
// 入参仿真Repository、UID、端口、上下行、设备偏移
// 输出:公里标信息
func CalcTrainKilometer(repo *repository.Repository, uid, port string, runDirection bool, offset int64) (
km *proto2.Kilometer) {
if port == "" {
km = sectionOffsetToKilometer(repo, uid, runDirection, offset)
} else {
km = turnoutOffsetToKilometer(repo, uid, runDirection, offset)
}
return
}
// 获取区段的上公里标
// 入参仿真Repository、UID、上下行、设备偏移
// 输出:公里标信息
func sectionOffsetToKilometer(repo *repository.Repository, uid string, runDirection bool, offset int64) (km *proto2.Kilometer) {
section := repo.FindPhysicalSection(uid)
if section == nil {
panic(sys_error.New(fmt.Sprintf("地图不存在uid:%s缓存", uid)))
}
km = convertRepoBaseKm(repo, section.AKilometer())
if runDirection {
km.Value = km.Value + offset
} else {
km.Value = km.Value - offset
}
return
}
// 获取道岔的公里标
// 入参仿真Repository、UID、上下行、设备偏移
// 输出:公里标信息
func turnoutOffsetToKilometer(repo *repository.Repository, uid string, runDirection bool, offset int64) (km *proto2.Kilometer) {
turnout := repo.FindTurnout(uid)
if turnout == nil {
panic(sys_error.New(fmt.Sprintf("不存在道岔【uid:%s】", uid)))
}
// 岔心公里标
km = convertRepoBaseKm(repo, turnout.GetTurnoutKm(proto2.Port_None))
if runDirection {
km.Value = km.Value + offset
} else {
km.Value = km.Value - offset
}
return
}
// 根据起始与末尾端查找经过的设备ID列表
// 入参起始设备uid、起始设备端口、终点设备uid、在区段a->b或道岔->岔心)上的找寻方向
// 输出占用物理区段的ID
func QueryDeviceRoutePath(sim *VerifySimulation, startUid, endUid, startPort string, pointTo bool) []string {
suid, euid, port, direction := startUid, endUid, startPort, pointTo
var paths []string
for {
device := sim.Repo.FindById(suid)
var nextRelation repository.DevicePort
switch device.Type() {
case proto2.DeviceType_DeviceType_PhysicalSection: // 区段
d := device.(*repository.PhysicalSection)
paths = append(paths, d.Id())
nextRelation = d.ARelation()
if direction {
nextRelation = d.BRelation()
}
case proto2.DeviceType_DeviceType_Turnout: // 道岔
d := device.(*repository.Turnout)
if d.GetPhysicalSection() == nil {
panic(sys_error.New(fmt.Sprintf("道岔%s没有绑定物理区段", device.Id())))
}
paths = append(paths, d.GetPhysicalSection().Id())
var nextPort proto2.Port
switch port {
case "A":
nextPort = proto2.Port_A
case "B":
nextPort = proto2.Port_B
case "C":
nextPort = proto2.Port_C
}
if pointTo { // -> 岔心
nextPort = getTurnoutNextPort(sim, d.Id(), port)
}
nextRelation = d.GindDevicePortByPort(nextPort)
default:
panic(sys_error.New(fmt.Sprintf("查找路径设备类型%s无处理处理", device.Type().String())))
}
if nextRelation == nil {
break
}
if suid == euid {
break
}
suid = nextRelation.Device().Id() // 下一个设备ID
port = nextRelation.Port().String() // 下一个道岔端
switch nextRelation.Device().Type() { // 查找方向
case proto2.DeviceType_DeviceType_PhysicalSection:
direction = nextRelation.Port() == proto2.Port_A
case proto2.DeviceType_DeviceType_Turnout:
direction = true
}
}
return paths
}