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

1284 lines
40 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 (
"encoding/json"
"fmt"
"log/slog"
"math"
"sort"
"strconv"
"strings"
"sync"
"time"
"joylink.club/bj-rtsts-server/config"
"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/message"
"joylink.club/bj-rtsts-server/third_party/semi_physical_train"
"joylink.club/bj-rtsts-server/ts/protos/graphicData"
"joylink.club/bj-rtsts-server/ts/protos/state"
"joylink.club/ecs"
"joylink.club/rtsssimulation/fi"
"joylink.club/rtsssimulation/repository"
"joylink.club/rtsssimulation/repository/model/proto"
)
// 轨旁仿真定义
type VerifySimulation struct {
//地图id
MapIds []int32
// 项目ID
ProjectId int32
//仿真id
SimulationId string
//仿真内存数据
Memory *WaysideMemory
//模型仓库
Repo *repository.Repository
//Rtss仿真世界的
World ecs.World
//设备UID映射
uidMap map[string]*elementIdStructure
// 运行环境配置
runConfig *config.ThridPartyConfig
// 运行状态
runState state.SimulationStatus_SimulationState
}
// 轨旁仿真内存模型
type WaysideMemory struct {
//可变状态数据:轨旁仿真模型状态(全量数据)
Status *VerifyStatus
// 要变更的状态:用户操作过的状态记录在这里,增量推送次数据
ChangeStatus *ChangeVerifyStatus
//状态保护锁
rwLock *sync.RWMutex
}
// 轨旁仿真模型状态
type VerifyStatus struct {
//道岔状态key为道岔id即索引 state.SwitchState
SwitchStateMap sync.Map
//轨道状态key为轨道id即索引 state.LinkState
LinkStateMap sync.Map
//列车状态key为列车id即索引 state.TrainState
TrainStateMap sync.Map
//计轴区段状态key为计轴区段的id即索引 state.SectionState
AxleSectionStateMap sync.Map
//物理区段状态key为物理区段id即索引 state.SectionState
PhysicalSectionStateMap sync.Map
//逻辑区段状态key为逻辑区段id即索引 state.SectionState
LogicSectionStateMap sync.Map
//信号机状态key为信号机id,即索引 state.SignalState
SignalStateMap sync.Map
}
// 轨旁仿真模型状态(变更)
type ChangeVerifyStatus struct {
VerifyStatus
//删除的列车ID列表
RemoveTrainId []string
}
func NewWaysideMemory() *WaysideMemory {
return &WaysideMemory{
Status: &VerifyStatus{},
ChangeStatus: &ChangeVerifyStatus{},
rwLock: &sync.RWMutex{},
}
}
// 创建仿真对象
func CreateSimulation(projectId int32, mapIds []int32, runConfig *dto.ProjectRunConfigDto) (*VerifySimulation, error) {
// 地图信息
sort.Slice(mapIds, func(i, j int) bool {
return QueryOnlyGiType(mapIds[i]) < QueryOnlyGiType(mapIds[j])
})
verifySimulation := &VerifySimulation{
ProjectId: projectId,
MapIds: mapIds,
runState: state.SimulationStatus_PAUSE,
}
// 设置运行环境
err := verifySimulation.initRunConfig(runConfig)
if err != nil {
return nil, err
}
// 构建Repository
err = verifySimulation.initRepository()
if err != nil {
return nil, err
}
return verifySimulation, nil
}
// 启动
func (s *VerifySimulation) Start() {
s.runState = state.SimulationStatus_START
}
// 销毁
func (s *VerifySimulation) Destroy() {
s.runState = state.SimulationStatus_DESTROY
}
// 暂停
func (s *VerifySimulation) Pause() {
s.runState = state.SimulationStatus_PAUSE
}
// 获取状态
func (s *VerifySimulation) GetRunState() state.SimulationStatus_SimulationState {
return s.runState
}
// 获取仿真世界信息
func (s *VerifySimulation) GetComIdByUid(uid string) string {
es := s.uidMap
if es == nil {
panic(&dto.ErrorDto{Code: dto.DataNotExist, Message: "无映射信息"})
}
if es[uid] == nil {
panic(&dto.ErrorDto{Code: dto.DataNotExist, Message: "无【uid】映射信息"})
}
return es[uid].CommonId
}
// 采集动力学道岔状态
func (s *VerifySimulation) CollectDynamicsTurnoutInfo() []*message.DynamicsTurnoutInfo {
stateSlice := GetAllTurnoutState(s)
var turnoutStates []*message.DynamicsTurnoutInfo
for _, sta := range stateSlice {
code64, err := strconv.ParseUint(sta.Id, 10, 16)
if err != nil {
slog.Error("id转uint16报错", err)
}
info := message.DynamicsTurnoutInfo{
Code: uint16(code64),
NPosition: sta.Dw,
RPosition: sta.Fw,
}
turnoutStates = append(turnoutStates, &info)
}
return turnoutStates
}
// 处理动力学列车速度消息
func (s *VerifySimulation) HandleDynamicsTrainInfo(info *message.DynamicsTrainInfo) {
sta, ok := s.Memory.Status.TrainStateMap.Load(strconv.Itoa(int(info.Number)))
if !ok {
return
}
// 给半实物仿真发送速度
semi_physical_train.Default().SendTrainControlMessage(info)
// 更新列车状态
UpdateTrainState(s, convert(info, sta.(*state.TrainState), s))
}
func convert(info *message.DynamicsTrainInfo, sta *state.TrainState, simulation *VerifySimulation) *state.TrainState {
delayTime := time.Now().UnixMilli() - sta.VobcState.UpdateTime
sta.ControlDelayTime = (int64(sta.VobcState.LifeSignal)-int64(info.VobcLifeSignal))*20 + delayTime
slog.Debug("收到动力学原始消息", "Number", info.Number, "Link", info.Link, "LinkOffset", info.LinkOffset)
id, port, offset, runDirection, pointTo, kilometer := QueryDeviceByCalcLink(simulation.Repo, strconv.Itoa(int(info.Link)), int64(info.LinkOffset), info.Up)
slog.Debug("处理动力学转换后的消息", "number", info.Number, "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo)
sta.HeadDeviceUId = id
sta.HeadDeviceId = simulation.GetComIdByUid(id)
sta.DevicePort = port
sta.HeadOffset = offset
sta.PointTo = pointTo
sta.TrainKilometer = kilometer
sta.RunDirection = runDirection
//判定车头方向
sta.HeadDirection = runDirection
if sta.VobcState != nil {
if sta.VobcState.DirectionForward {
sta.HeadDirection = runDirection
} else if sta.VobcState.DirectionBackward {
sta.HeadDirection = !runDirection
}
}
if info.Speed < 0 {
sta.RunDirection = !sta.RunDirection
}
// 赋值动力学信息
sta.DynamicState.Heartbeat = int32(info.LifeSignal)
sta.DynamicState.HeadLinkId = strconv.Itoa(int(info.Link))
sta.DynamicState.HeadLinkOffset = int64(info.LinkOffset)
sta.DynamicState.Slope = int32(info.Slope)
sta.DynamicState.Upslope = info.UpSlope
sta.DynamicState.RunningUp = info.Up
sta.DynamicState.RunningResistanceSum = float32(info.TotalResistance) / 1000
sta.DynamicState.AirResistance = float32(info.AirResistance) / 1000
sta.DynamicState.RampResistance = float32(info.SlopeResistance) / 1000
sta.DynamicState.CurveResistance = float32(info.CurveResistance) / 1000
sta.DynamicState.Speed = speedParse(info.Speed)
sta.DynamicState.HeadSensorSpeed1 = speedParse(info.HeadSpeed1)
sta.DynamicState.HeadSensorSpeed2 = speedParse(info.HeadSpeed2)
sta.DynamicState.TailSensorSpeed1 = speedParse(info.TailSpeed1)
sta.DynamicState.TailSensorSpeed2 = speedParse(info.TailSpeed2)
sta.DynamicState.HeadRadarSpeed = speedParse(info.HeadRadarSpeed)
sta.DynamicState.TailRadarSpeed = speedParse(info.TailRadarSpeed)
sta.DynamicState.Acceleration = info.Acceleration
return sta
}
// 获取动力学配置信息
func (s *VerifySimulation) GetDynamicsRunConfig() *config.DynamicsConfig {
return &s.runConfig.Dynamics
}
// 获取动力学运行资源
func (s *VerifySimulation) GetDynamicsRunRepository() *message.LineBaseInfo {
info := &message.LineBaseInfo{}
for _, model := range s.Repo.LinkList() {
id, _ := strconv.Atoi(model.Id())
link := &message.Link{
ID: int32(id),
Len: int32(model.Length()),
}
info.LinkList = append(info.LinkList, link)
if model.ARelation() != nil {
turnoutId, _ := strconv.Atoi(s.GetComIdByUid(model.ARelation().Device().Id()))
link.ARelTurnoutId = int32(turnoutId)
switch model.ARelation().Port() {
case proto.Port_A:
link.ARelTurnoutPoint = "A"
case proto.Port_B:
link.ARelTurnoutPoint = "B"
case proto.Port_C:
link.ARelTurnoutPoint = "C"
}
}
if model.BRelation() != nil {
turnoutId, _ := strconv.Atoi(s.GetComIdByUid(model.BRelation().Device().Id()))
link.BRelTurnoutId = int32(turnoutId)
switch model.BRelation().Port() {
case proto.Port_A:
link.BRelTurnoutPoint = "A"
case proto.Port_B:
link.BRelTurnoutPoint = "B"
case proto.Port_C:
link.BRelTurnoutPoint = "C"
}
}
}
for _, model := range s.Repo.SlopeList() {
id, _ := strconv.Atoi(s.GetComIdByUid(model.Id()))
slope := &message.Slope{
ID: int32(id),
StartLinkOffset: int32(model.StartLinkPosition().Offset()),
EndLinkOffset: int32(model.EndLinkPosition().Offset()),
DegreeTrig: model.Degree(),
}
info.SlopeList = append(info.SlopeList, slope)
startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id())
slope.StartLinkId = int32(startLinkId)
endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id())
slope.EndLinkId = int32(endLinkId)
}
for _, model := range s.Repo.SectionalCurvatureList() {
id, _ := strconv.Atoi(s.GetComIdByUid(model.Id()))
curve := &message.Curve{
ID: int32(id),
StartLinkOffset: int32(model.StartLinkPosition().Offset()),
EndLinkOffset: int32(model.EndLinkPosition().Offset()),
Curvature: model.Radius(),
}
info.CurveList = append(info.CurveList, curve)
startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id())
curve.StartLinkId = int32(startLinkId)
endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id())
curve.EndLinkId = int32(endLinkId)
}
return info
}
// 发送给前端的速度格式化
func speedParse(speed float32) int32 {
return int32(math.Abs(float64(speed * 3.6 * 100)))
}
// 处理半实物仿真列车控制消息
func (s *VerifySimulation) HandleSemiPhysicalTrainControlMsg(b []byte) {
s.Memory.Status.TrainStateMap.Range(func(_, value any) bool {
train := value.(*state.TrainState)
if !train.Show { // 下线列车
return false
}
trainId, err := strconv.Atoi(train.Id)
if err != nil {
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
}
d := append(b, uint8(trainId))
// 发送给动力学
dynamics.Default().SendTrainControlMessage(d)
// 存放至列车中
controlMessage := &message.TrainControlMsg{}
controlMessage.Decode(b)
train.VobcState = controlMessage.ControlInfo
return true
})
}
// 获取半实物运行配置信息
func (s *VerifySimulation) GetSemiPhysicalRunConfig() *config.VobcConfig {
return &s.runConfig.Vobc
}
// 获取所有联锁配置唯一Code
func (s *VerifySimulation) GetInterlockCodes() []*config.InterlockConfig {
stationMap := make(map[string]string)
for _, station := range s.Repo.StationList() {
stationMap[station.GetCode()] = station.Id()
}
var configs []*config.InterlockConfig
for _, c := range s.runConfig.Interlocks {
if stationMap[c.Code] == "" || !c.Open {
continue
}
configs = append(configs, &config.InterlockConfig{
Code: stationMap[c.Code],
Ip: c.Ip,
LocalPort: c.LocalPort,
RemotePort: c.RemotePort,
Open: c.Open,
})
}
return configs
}
// 处理接到的联锁消息
func (s *VerifySimulation) HandleInterlockDriverInfo(code string, b []byte) {
for _, m := range s.Repo.CentralizedList() { // 获取继电器地图信息
if m.StationId != code {
continue
}
driverMsg := message.NewInterlockReceiveMsgPkg(0, len(m.QdList), len(m.TransponderId))
driverMsg.Decode(b)
driveState := driverMsg.DriveInfo
for i, r := range m.QdList {
ds := driveState[i]
for _, b := range r.RefRelays {
slog.Debug("继电器位【%s】获取到驱动状态【%v】", b, ds)
}
}
return
}
}
// 采集联锁中的继电器消息
func (s *VerifySimulation) CollectInterlockRelayInfo(code string) *message.InterlockSendMsgPkg {
for _, m := range s.Repo.CentralizedList() { // 获取继电器地图信息
if m.StationId != code {
continue
}
if len(m.CjList) == 0 {
return nil
}
collectInfo := make([]bool, len(m.CjList))
for i, l := range m.CjList {
if l == nil || len(l.RefRelays) == 0 {
continue
}
rs := true
for _, j := range l.RefRelays {
if j.Position == proto.CjDataItem_Q {
rs = rs && fi.CollectXQCircuitState(s.World, j.RelayId)
} else {
rs = rs && fi.CollectLXCircuitState(s.World, j.RelayId)
}
if !rs {
break
}
}
collectInfo[i] = rs
}
return &message.InterlockSendMsgPkg{Info: collectInfo}
}
return nil
}
// 初始化仿真运行配置
func (s *VerifySimulation) initRunConfig(runConfig *dto.ProjectRunConfigDto) error {
if runConfig == nil || runConfig.ConfigContent == "" {
return nil
}
var configMap config.ThridPartyConfig
err := json.Unmarshal([]byte(runConfig.ConfigContent), &configMap)
if err != nil {
return sys_error.New("配置信息格式错误", err)
}
s.runConfig = &configMap
s.runConfig.Id = runConfig.Id
return nil
}
// 获取仿真运行参数ID
func (s *VerifySimulation) GetRunConfigId() int32 {
if s.runConfig == nil {
return 0
}
return s.runConfig.Id
}
// 初始化运行资源
func (s *VerifySimulation) initRepository() error {
// 构建Repository
var mapIdStrSlice []string
for _, id := range s.MapIds {
mapIdStrSlice = append(mapIdStrSlice, strconv.Itoa(int(id)))
}
repoId := strings.Join(mapIdStrSlice, "|")
repoVersion := "0.1"
repo := repository.FindRepository(repoId, repoVersion)
if repo == nil {
protoRepo, err := buildProtoRepository(s.MapIds)
if err != nil {
return sys_error.New("数据错误", err)
}
protoRepo.Id, protoRepo.Version = repoId, repoVersion
newRepo, err := repository.BuildRepository(protoRepo)
if err != nil {
return sys_error.New("数据错误", err)
}
repo = newRepo
}
s.Repo = repo
s.Memory = NewWaysideMemory()
// 构建所有UID映射关系
s.uidMap = buildRepositoryAllUidsMap(s.MapIds, s.Repo)
return nil
}
func buildProtoRepository(mapIds []int32) (*proto.Repository, error) {
repo := &proto.Repository{}
var exceptStationGiMapIds []int32
//创建设备
for _, mapId := range mapIds {
giType := QueryGiType(mapId)
if giType == graphicData.PictureType_StationLayout {
stationGi := QueryGiData[*graphicData.RtssGraphicStorage](mapId)
fillProtoRepository(repo, stationGi, mapId)
} else {
exceptStationGiMapIds = append(exceptStationGiMapIds, mapId)
}
}
//构建并关联电子元件
for _, mapId := range exceptStationGiMapIds {
giType := QueryGiType(mapId)
if giType == graphicData.PictureType_RelayCabinetLayout {
relayGi := QueryGiData[*graphicData.RelayCabinetGraphicStorage](mapId)
buildAndRelateElectronicComponent(repo, relayGi, mapId)
}
}
return repo, nil
}
func buildAndRelateElectronicComponent(repo *proto.Repository, relayGi *graphicData.RelayCabinetGraphicStorage, mapId int32) {
uidsMap := QueryUidStructure[*RelayUidStructure](mapId)
city := relayGi.UniqueIdPrefix.City
lineId := relayGi.UniqueIdPrefix.LineId
station := relayGi.UniqueIdPrefix.BelongsConcentrationStation
stationUid := GenerateElementUid(city, lineId, nil, station)
relayMap := make(map[string]*proto.Relay)
for _, relay := range relayGi.Relays {
repoRelay := &proto.Relay{
Id: uidsMap.RelayIds[relay.Common.Id].Uid,
Code: relay.Code,
Model: convertRelayModel(relay.NewModel),
StationId: stationUid,
}
repo.Relays = append(repo.Relays, repoRelay)
relayMap[repoRelay.Id] = repoRelay
}
for _, pfp := range relayGi.PhaseFailureProtectors {
repo.PhaseFailureProtectors = append(repo.PhaseFailureProtectors, &proto.PhaseFailureProtector{
Id: uidsMap.RelayIds[pfp.Common.Id].Uid,
Code: pfp.Code,
})
}
turnoutMap := make(map[string]*proto.Turnout)
for _, turnout := range repo.Turnouts {
turnoutMap[turnout.Id] = turnout
}
signalMap := make(map[string]*proto.Signal)
for _, signal := range repo.Signals {
signalMap[signal.Id] = signal
}
stationMap := make(map[string]*proto.Station)
for _, station := range repo.Stations {
stationMap[station.Id] = station
}
psdMap := make(map[string]*proto.Psd)
for _, psd := range repo.Psds {
psdMap[psd.Id] = psd
}
platformMap := make(map[string]*proto.Platform)
for _, platform := range repo.Platforms {
platformMap[platform.Id] = platform
}
for _, relationship := range relayGi.DeviceRelateRelayList {
switch relationship.DeviceType {
case graphicData.RelatedRef_Turnout:
turnout := turnoutMap[GenerateElementUid(city, lineId, []string{station}, relationship.Code)]
if turnout == nil {
continue
}
for _, group := range relationship.Combinationtypes {
var componentIds []string
for _, relayId := range group.RefRelays {
if uidsMap.RelayIds[relayId] == nil {
continue
}
componentIds = append(componentIds, uidsMap.RelayIds[relayId].Uid)
}
turnout.ElectronicComponentGroups = append(turnout.ElectronicComponentGroups,
&proto.ElectronicComponentGroup{
Code: group.Code,
ComponentIds: componentIds,
})
}
case graphicData.RelatedRef_signal:
signal := signalMap[GenerateElementUid(city, lineId, []string{station}, relationship.Code)]
if signal == nil {
continue
}
//信号机只有一个组合类型
if len(relationship.Combinationtypes) != 1 {
panic(fmt.Sprintf("信号机[%s]须有一个组合类型", signal.Id))
}
group := relationship.Combinationtypes[0]
var componentIds []string
for _, relayId := range group.RefRelays {
if uidsMap.RelayIds[relayId] == nil {
continue
}
componentIds = append(componentIds, uidsMap.RelayIds[relayId].Uid)
}
signal.Code = group.Code
signal.ElectronicComponentGroups = append(signal.ElectronicComponentGroups,
&proto.ElectronicComponentGroup{
Code: group.Code,
ComponentIds: componentIds,
})
case graphicData.RelatedRef_station:
station := stationMap[GenerateElementUid(city, lineId, nil, relationship.Code)]
if station == nil {
continue
}
for _, group := range relationship.Combinationtypes {
d := &proto.ElectronicGroup{Code: group.Code}
for _, relayId := range group.RefRelays {
if uidsMap.RelayIds[relayId] == nil {
continue
}
d.Components = append(d.Components, &proto.ElectronicComponent{
Id: uidsMap.RelayIds[relayId].Uid,
DeviceType: proto.DeviceType_DeviceType_Relay,
})
}
station.ElectronicGroup = append(station.ElectronicGroup, d)
}
case graphicData.RelatedRef_ScreenDoor:
psd, ok := psdMap[GenerateElementUid(city, lineId, nil, relationship.Code)]
if !ok {
continue
}
for _, group := range relationship.Combinationtypes {
var componentIds []string
for _, relayId := range group.RefRelays {
if uidsMap.RelayIds[relayId] == nil {
continue
}
componentIds = append(componentIds, uidsMap.RelayIds[relayId].Uid)
}
psd.ElectronicComponentGroups = append(psd.ElectronicComponentGroups,
&proto.ElectronicComponentGroup{
Code: group.Code,
ComponentIds: componentIds,
})
}
}
}
//门控箱
for _, mkx := range repo.Mkxs {
platform := platformMap[psdMap[mkx.PsdId].GetPlatformId()]
station := stationMap[platform.GetStationId()]
var s string
if strings.Contains(platform.GetCode(), "上行") {
s = "S"
} else if strings.Contains(platform.GetCode(), "下行") {
s = "X"
} else {
continue
}
for _, group := range station.ElectronicGroup {
if group.Code == "MKX" {
for _, component := range group.Components {
relay := relayMap[component.Id]
if strings.Contains(relay.GetCode(), s) {
if strings.Contains(relay.GetCode(), "PCB") {
mkx.PcbjId = relay.Id
} else if strings.Contains(relay.GetCode(), "POB") {
mkx.PobjId = relay.Id
} else if strings.Contains(relay.GetCode(), "PAB") {
mkx.PabjId = relay.Id
}
}
}
}
}
}
// 处理该集中站采集、驱动配置信息
centralizedStationId := GenerateElementUid(city, lineId, nil, station)
ref := queryCentralizedStationRef(centralizedStationId, repo)
ref.CjList = append(ref.CjList, handlerRelayGiCj(uidsMap, centralizedStationId, relayGi.CiCjList)...)
ref.QdList = append(ref.QdList, handlerRelayGiQd(uidsMap, centralizedStationId, relayGi.CiQdList)...)
}
// 查询集中站配置信息
func queryCentralizedStationRef(stationId string, repo *proto.Repository) *proto.CentralizedStationRef {
var ref *proto.CentralizedStationRef
for _, r := range repo.CentralizedStationRefs {
if r.StationId == stationId {
ref = r
break
}
}
if ref == nil {
ref = &proto.CentralizedStationRef{StationId: stationId}
repo.CentralizedStationRefs = append(repo.CentralizedStationRefs, ref)
}
return ref
}
// 处理继电器采集信息
func handlerRelayGiCj(uidsMap *RelayUidStructure, stationId string, ciCj *graphicData.CiCj) []*proto.CjData {
if ciCj == nil {
return nil
}
// 采集信息
dataLenght := len(ciCj.CjList) * int(ciCj.DsCount)
if dataLenght == 0 {
return nil
}
cjList := make([]*proto.CjData, dataLenght)
index := 0
for ci, col := range ciCj.CjList {
for ri, row := range col.BitList {
if len(row.RefRelays) == 0 {
index++
continue
}
r := &proto.CjData{Row: int32(ri), Col: int32(ci)}
for _, j := range row.RefRelays {
u := uidsMap.RelayIds[j.RelayId]
if u == nil {
panic(sys_error.New(fmt.Sprintf("集中站【id:%s】不存在继电器的【comId:%s】UID映射关系", stationId, j.RelayId)))
}
d := &proto.CjDataItem{RelayId: u.Uid}
if j.Position == graphicData.CjDataItem_H {
d.Position = proto.CjDataItem_H
} else {
d.Position = proto.CjDataItem_Q
}
r.RefRelays = append(r.RefRelays, d)
}
cjList[index] = r
index++
}
}
return cjList
}
// 处理继电器驱动信息
func handlerRelayGiQd(uidsMap *RelayUidStructure, stationId string, ciQd *graphicData.CiQd) []*proto.QdData {
if ciQd == nil {
return nil
}
// 驱动信息
dataLenght := len(ciQd.QdList) * int(ciQd.DsCount)
if dataLenght == 0 {
return nil
}
qdList := make([]*proto.QdData, dataLenght)
index := 0
for ci, col := range ciQd.QdList {
for ri, row := range col.BitList {
if len(row.RefRelays) == 0 {
index++
continue
}
r := &proto.QdData{Row: int32(ri), Col: int32(ci)}
for _, j := range row.RefRelays {
u := uidsMap.RelayIds[j]
if u == nil {
panic(sys_error.New(fmt.Sprintf("集中站【id:%s】不存在继电器的【comId:%s】UID映射关系", stationId, j)))
}
r.RefRelays = append(r.RefRelays, u.Uid)
}
qdList[index] = r
index++
}
}
return qdList
}
func convertRelayModel(modelType graphicData.Relay_ModelType) proto.Relay_Model {
switch modelType {
case graphicData.Relay_Unknown:
return proto.Relay_Unknown
case graphicData.Relay_JPXC_1000:
return proto.Relay_JPXC_1000
case graphicData.Relay_JPXC_1700:
return proto.Relay_JPXC_1700
case graphicData.Relay_JWJXC_480:
return proto.Relay_JWJXC_480
case graphicData.Relay_JWJXC_H125_80:
return proto.Relay_JWJXC_H125_80
case graphicData.Relay_JWXC_1700:
return proto.Relay_JWXC_1700
case graphicData.Relay_JWXC_H340:
return proto.Relay_JWXC_H340
case graphicData.Relay_JYJXC_160_260:
return proto.Relay_JYJXC_160_260
case graphicData.Relay_JZXC_H18:
return proto.Relay_JZXC_H18
default:
panic(fmt.Sprintf("意料之外的继电器型号:%s", modelType))
}
}
func fillProtoRepository(repo *proto.Repository, storage *graphicData.RtssGraphicStorage, mapId int32) {
repo.MainCoordinateSystem = storage.UniqueIdPrefix.MainCoordinateSystem
axleCountingMap := make(map[string]*graphicData.AxleCounting)
uidsMap := QueryUidStructure[*StationUidStructure](mapId)
for _, data := range storage.AxleCountings {
axleCountingMap[data.Common.Id] = data
cpType := proto.CheckPointType_AxleCounter
if data.Type == graphicData.AxleCounting_SectionBoundary {
cpType = proto.CheckPointType_Boundary
}
cp := &proto.CheckPoint{
Id: data.Common.Id,
Km: convertKm(data.KilometerSystem),
Type: cpType,
DevicePorts: convertDevicePorts(data.AxleCountingRef),
}
repo.CheckPoints = append(repo.CheckPoints, converCheckPointUid(cp, uidsMap))
}
for _, data := range storage.Section {
var turnoutIds []string
if data.SectionType == graphicData.Section_TurnoutPhysical {
turnoutIds = findTurnoutIds(axleCountingMap, data.AxleCountings)
}
centralizedStation := ""
if len(data.CentralizedStations) > 0 {
centralizedStation = data.CentralizedStations[0]
}
physicalSection := &proto.PhysicalSection{
Id: data.Common.Id,
ADevicePort: convertDevicePort(data.PaRef),
BDevicePort: convertDevicePort(data.PbRef),
TurnoutIds: turnoutIds,
CentralizedStation: centralizedStation,
}
repo.PhysicalSections = append(repo.PhysicalSections, converSectionUid(physicalSection, uidsMap))
}
for _, data := range storage.Turnouts {
var km *proto.Kilometer
for _, ks := range data.KilometerSystem {
if ks.Kilometer != 0 {
km = convertKm(ks)
break
}
}
var switchMachineType proto.Turnout_SwitchMachineType
switch data.SwitchMachineType {
case graphicData.Turnout_ZDJ9_Single:
switchMachineType = proto.Turnout_ZDJ9_Single
case graphicData.Turnout_ZDJ9_Double:
switchMachineType = proto.Turnout_ZDJ9_Double
}
turnout := &proto.Turnout{
Id: data.Common.Id,
Km: km,
ADevicePort: convertDevicePort(data.PaRef),
BDevicePort: convertDevicePort(data.PbRef),
CDevicePort: convertDevicePort(data.PcRef),
SwitchMachineType: switchMachineType,
}
repo.Turnouts = append(repo.Turnouts, converTurnoutUid(turnout, uidsMap))
}
for _, data := range storage.Signals {
var sectionId string
var turnoutPort *proto.DevicePort
switch data.RefDev.DeviceType {
case graphicData.RelatedRef_Section:
sectionId = data.RefDev.Id
case graphicData.RelatedRef_Turnout:
turnoutPort = convertDevicePort(data.RefDev)
}
signal := &proto.Signal{
Id: data.Common.Id,
Km: convertKm(data.KilometerSystem),
SectionId: sectionId,
TurnoutPort: turnoutPort,
Model: convertToProtoSignalModel(data.Mt),
}
repo.Signals = append(repo.Signals, converSignalUid(signal, uidsMap))
}
stm := make(map[string][]string)
for _, data := range storage.Transponders {
var sectionId string
var turnoutPort *proto.DevicePort
switch data.TransponderRef.DeviceType {
case graphicData.RelatedRef_Section:
sectionId = data.TransponderRef.Id
case graphicData.RelatedRef_Turnout:
turnoutPort = convertDevicePort(data.TransponderRef)
}
responder := &proto.Transponder{
Id: data.Common.Id,
Km: convertKm(data.KilometerSystem),
SectionId: sectionId,
TurnoutPort: turnoutPort,
}
repo.Transponders = append(repo.Transponders, converTransponderUid(responder, uidsMap))
for _, stationName := range data.CentralizedStations {
if stm[stationName] == nil {
stm[stationName] = []string{responder.Id}
} else {
stm[stationName] = append(stm[stationName], responder.Id)
}
}
}
slopeKsMap := make(map[string]*graphicData.SlopeKiloMarker)
for _, data := range storage.SlopeKiloMarker {
slopeKsMap[data.Common.Id] = data
}
curveKsMap := make(map[string]*graphicData.CurvatureKiloMarker)
for _, data := range storage.CurvatureKiloMarker {
curveKsMap[data.Common.Id] = data
}
for _, data := range storage.Slopes {
var kms []*proto.Kilometer
for _, id := range data.RefDeviceId {
kms = append(kms, convertKm(slopeKsMap[id].KilometerSystem[0]))
}
slope := &proto.Slope{
Id: data.Common.Id,
Kms: kms,
Degree: data.SlopeNumber,
}
repo.Slopes = append(repo.Slopes, converSlopeUid(slope, uidsMap))
}
for _, data := range storage.Curvatures {
var kms []*proto.Kilometer
for _, id := range data.RefDeviceId {
kms = append(kms, convertKm(curveKsMap[id].KilometerSystem[0]))
}
slope := &proto.SectionalCurvature{
Id: data.Common.Id,
Kms: kms,
Radius: data.CurvatureNumber,
}
repo.SectionalCurvatures = append(repo.SectionalCurvatures, converCurvatureUid(slope, uidsMap))
}
for _, data := range storage.KilometerConvertList {
repo.KilometerConverts = append(repo.KilometerConverts, &proto.KilometerConvert{
KmA: convertKm(data.KmA),
KmB: convertKm(data.KmB),
SameTrend: data.SameTrend,
})
}
// 初始化站场图按钮
for _, data := range storage.EsbButtons {
repo.Buttons = append(repo.Buttons, &proto.Button{
Id: uidsMap.ButtonIds[data.Common.Id].Uid,
Code: data.Code,
ButtonType: proto.Button_Reset_Press,
})
}
// 车站关联关系
relateMap := make(map[string]*graphicData.StationRelateDevice)
for _, data := range storage.StationRelateDeviceList {
relateMap[data.Code] = data
}
// 处理车站信息
for _, data := range storage.Stations {
station := &proto.Station{
Id: uidsMap.StationIds[data.Common.Id].Uid,
Code: data.StationName,
}
// 关联车站的设备
refs, ok := relateMap[data.Code]
if ok {
for _, c := range refs.Combinationtypes {
group := &proto.ElectronicGroup{Code: c.Code}
for _, d := range c.RefDevices {
var comp *proto.ElectronicComponent
if uidsMap.ButtonIds[d] != nil { // 目前只处理按钮
comp = &proto.ElectronicComponent{
Id: uidsMap.ButtonIds[d].Uid,
DeviceType: proto.DeviceType_DeviceType_Button,
}
} else {
continue
}
group.Components = append(group.Components, comp)
}
station.ElectronicGroup = append(station.ElectronicGroup, group)
}
}
// 处理车站关联IBP的设备
handlerIBPDeviceToStation(station, repo, data.RefIbpMapCode)
repo.Stations = append(repo.Stations, station)
// 处理集中站的信息
if stm[station.Code] != nil {
ref := queryCentralizedStationRef(station.Id, repo)
ref.TransponderId = append(ref.TransponderId, stm[station.Code]...)
}
}
//门控箱
for _, data := range storage.GateBoxs {
mkx := &proto.Mkx{
Id: uidsMap.GateBoxIds[data.Common.Id].Uid,
PsdId: uidsMap.PsdIds[data.RefScreenDoor].Uid,
}
repo.Mkxs = append(repo.Mkxs, mkx)
pslMapId := QueryGiId(data.RefGatedBoxMapCode)
pslStorage := QueryGiData[*graphicData.PslGraphicStorage](pslMapId)
for _, button := range pslStorage.PslButtons {
repoButton := &proto.Button{
Id: uidsMap.GateBoxIds[data.Common.Id].Uid + "_" + button.Code,
Code: button.Code,
ButtonType: proto.Button_Reset_Press,
HasLight: true,
}
repo.Buttons = append(repo.Buttons, repoButton)
switch button.Code {
case "PCB":
mkx.PcbButtonId = repoButton.Id
case "POB":
mkx.PobButtonId = repoButton.Id
case "PAB":
mkx.PabButtonId = repoButton.Id
}
}
}
//站台
platformMap := make(map[string]*graphicData.Platform)
for _, data := range storage.Platforms {
platformMap[data.Common.Id] = data
platform := &proto.Platform{
Id: uidsMap.PlatformIds[data.Common.Id].Uid,
Code: data.Code,
}
repo.Platforms = append(repo.Platforms, platform)
platform.StationId = uidsMap.StationIds[data.RefStationId].Uid
platform.PhysicalSectionId = uidsMap.PhysicalSectionIds[data.RefSectionId].Uid
}
//屏蔽门
for _, data := range storage.ScreenDoors {
var asdGroups []*proto.AsdGroup
for _, group := range data.ScreenDoorGroupList {
asdGroups = append(asdGroups, &proto.AsdGroup{
Group: group.TrainGroupAmount,
Start: group.StartSmallDoor,
End: group.EndSmallDoor,
})
}
psd := &proto.Psd{
Id: uidsMap.PsdIds[data.Common.Id].Uid,
AsdAmount: data.SonDoorAmount,
AsdGroups: asdGroups,
PlatformId: uidsMap.PlatformIds[data.RefPlatformId].Uid,
}
repo.Psds = append(repo.Psds, psd)
}
}
// 将IBP的设备关联到车站中
func handlerIBPDeviceToStation(station *proto.Station, repo *proto.Repository, ibpMap string) {
storage := GetStorageIBPMapData(ibpMap)
if storage == nil {
return
}
deviceMap := make(map[string]*proto.ElectronicComponent) // 对应的设备类型
for _, data := range storage.IbpButtons { // 处理按钮
buttonType := proto.Button_NO_Reset_Press
if data.IsSelfReset {
buttonType = proto.Button_Reset_Press
}
b := &proto.Button{
Id: station.Id + "_button_" + data.Code,
Code: data.Code,
ButtonType: buttonType,
HasLight: data.HasLight,
}
deviceMap[data.Common.Id] = &proto.ElectronicComponent{
Id: b.Id,
DeviceType: proto.DeviceType_DeviceType_Button,
}
repo.Buttons = append(repo.Buttons, b)
}
for _, data := range storage.IbpKeys { // 钥匙
b := &proto.Key{
Id: station.Id + "_key_" + data.Code,
Code: data.Code,
Gear: 2,
}
deviceMap[data.Common.Id] = &proto.ElectronicComponent{
Id: b.Id,
DeviceType: proto.DeviceType_DeviceType_Key,
}
repo.Keys = append(repo.Keys, b)
}
for _, data := range storage.IbpAlarms { // 报警器
b := &proto.Alarm{
Id: station.Id + "_alarm_" + data.Code,
Code: data.Code,
}
deviceMap[data.Common.Id] = &proto.ElectronicComponent{
Id: b.Id,
DeviceType: proto.DeviceType_DeviceType_Alarm,
}
repo.Alarms = append(repo.Alarms, b)
}
empGroup := &proto.ElectronicGroup{Code: "EMP"}
spksGroup := &proto.ElectronicGroup{Code: "SPKS"}
for _, data := range storage.IbpLights { // 指示灯,
b := &proto.Light{
Id: station.Id + "_light_" + data.Code,
Code: data.Code,
}
repo.Lights = append(repo.Lights, b)
// 存入组合类型的
c := &proto.ElectronicComponent{
Id: b.Id,
DeviceType: proto.DeviceType_DeviceType_Light,
}
switch data.Code {
case "X紧急关闭", "S紧急关闭":
empGroup.Components = append(empGroup.Components, c)
case "启用状态_SPKS1", "启用状态_SPKS3", "启用状态_SPKS2", "启用状态_SPKS4":
spksGroup.Components = append(spksGroup.Components, c)
}
}
for _, data := range storage.IbpRelatedDevices { // 组信息
for _, c := range data.Combinationtypes {
var group *proto.ElectronicGroup
switch c.Code {
case "EMP":
group = empGroup
case "SPKS":
group = spksGroup
default:
continue
}
for _, d := range c.RefDevices {
deviceType, ok := deviceMap[d]
if !ok {
slog.Debug("IBP组合类型类型不确定id:%s", d)
continue
}
group.Components = append(group.Components, deviceType)
}
}
}
station.ElectronicGroup = append(station.ElectronicGroup, empGroup, spksGroup)
}
func converCheckPointUid(data *proto.CheckPoint, uidsMap *StationUidStructure) *proto.CheckPoint {
data.Id = uidsMap.AxlePointIds[data.Id].Uid
for _, c := range data.DevicePorts {
c.DeviceId = converRefUid(c.DeviceId, c.DeviceType, uidsMap)
}
return data
}
func converSectionUid(data *proto.PhysicalSection, uidsMap *StationUidStructure) *proto.PhysicalSection {
data.Id = uidsMap.PhysicalSectionIds[data.Id].Uid
if data.ADevicePort != nil {
data.ADevicePort.DeviceId = converRefUid(data.ADevicePort.DeviceId, data.ADevicePort.DeviceType, uidsMap)
}
if data.BDevicePort != nil {
data.BDevicePort.DeviceId = converRefUid(data.BDevicePort.DeviceId, data.BDevicePort.DeviceType, uidsMap)
}
tids := make([]string, len(data.TurnoutIds))
for i, tid := range data.TurnoutIds {
tids[i] = converRefUid(tid, proto.DeviceType_DeviceType_Turnout, uidsMap)
}
data.TurnoutIds = tids
return data
}
func converTurnoutUid(data *proto.Turnout, uidsMap *StationUidStructure) *proto.Turnout {
data.Id = uidsMap.TurnoutIds[data.Id].Uid
if data.ADevicePort != nil {
data.ADevicePort.DeviceId = converRefUid(data.ADevicePort.DeviceId, data.ADevicePort.DeviceType, uidsMap)
}
if data.BDevicePort != nil {
data.BDevicePort.DeviceId = converRefUid(data.BDevicePort.DeviceId, data.BDevicePort.DeviceType, uidsMap)
}
if data.CDevicePort != nil {
data.CDevicePort.DeviceId = converRefUid(data.CDevicePort.DeviceId, data.CDevicePort.DeviceType, uidsMap)
}
return data
}
func converSignalUid(data *proto.Signal, uidsMap *StationUidStructure) *proto.Signal {
data.Id = uidsMap.SignalIds[data.Id].Uid
if data.SectionId != "" {
data.SectionId = converRefUid(data.SectionId, proto.DeviceType_DeviceType_PhysicalSection, uidsMap)
}
if data.TurnoutPort != nil {
data.TurnoutPort.DeviceId = converRefUid(data.TurnoutPort.DeviceId, data.TurnoutPort.DeviceType, uidsMap)
}
return data
}
func converTransponderUid(data *proto.Transponder, uidsMap *StationUidStructure) *proto.Transponder {
data.Id = uidsMap.TransponderIds[data.Id].Uid
if data.SectionId != "" {
data.SectionId = converRefUid(data.SectionId, proto.DeviceType_DeviceType_PhysicalSection, uidsMap)
}
if data.TurnoutPort != nil {
data.TurnoutPort.DeviceId = converRefUid(data.TurnoutPort.DeviceId, data.TurnoutPort.DeviceType, uidsMap)
}
return data
}
func converSlopeUid(data *proto.Slope, uidsMap *StationUidStructure) *proto.Slope {
data.Id = uidsMap.SlopeIds[data.Id].Uid
return data
}
func converCurvatureUid(data *proto.SectionalCurvature, uidsMap *StationUidStructure) *proto.SectionalCurvature {
data.Id = uidsMap.CurvatureIds[data.Id].Uid
return data
}
func converRefUid(id string, d proto.DeviceType, uidsMap *StationUidStructure) string {
var elementId *elementIdStructure
switch d {
case proto.DeviceType_DeviceType_CheckPoint:
elementId = uidsMap.AxlePointIds[id]
case proto.DeviceType_DeviceType_PhysicalSection:
elementId = uidsMap.PhysicalSectionIds[id]
case proto.DeviceType_DeviceType_SectionalCurvature:
elementId = uidsMap.CurvatureIds[id]
case proto.DeviceType_DeviceType_Signal:
elementId = uidsMap.SignalIds[id]
case proto.DeviceType_DeviceType_Slope:
elementId = uidsMap.SlopeIds[id]
case proto.DeviceType_DeviceType_Transponder:
elementId = uidsMap.TransponderIds[id]
case proto.DeviceType_DeviceType_Turnout:
elementId = uidsMap.TurnoutIds[id]
default:
panic(&dto.ErrorDto{Code: dto.ArgumentParseError, Message: "异常的设备类型-" + d.String()})
}
return elementId.Uid
}
func convertKm(ks *graphicData.KilometerSystem) *proto.Kilometer {
var dir proto.Direction
switch ks.Direction {
case graphicData.Direction_LEFT:
dir = proto.Direction_LEFT
case graphicData.Direction_RIGHT:
dir = proto.Direction_RIGHT
}
return &proto.Kilometer{
Value: ks.Kilometer,
CoordinateSystem: ks.CoordinateSystem,
Direction: dir,
}
}
func convertDevicePort(ref *graphicData.RelatedRef) *proto.DevicePort {
if ref == nil {
return nil
}
var deviceType proto.DeviceType
var port proto.Port
switch ref.DevicePort {
case graphicData.RelatedRef_A:
port = proto.Port_A
case graphicData.RelatedRef_B:
port = proto.Port_B
case graphicData.RelatedRef_C:
port = proto.Port_C
}
switch ref.DeviceType {
case graphicData.RelatedRef_Section:
deviceType = proto.DeviceType_DeviceType_PhysicalSection
case graphicData.RelatedRef_Turnout:
deviceType = proto.DeviceType_DeviceType_Turnout
default:
panic(fmt.Sprintf("异常的设备类型-%s", ref.DeviceType))
}
return &proto.DevicePort{
DeviceId: ref.Id,
DeviceType: deviceType,
Port: port,
}
}
func convertDevicePorts(refList []*graphicData.RelatedRef) []*proto.DevicePort {
var dps []*proto.DevicePort
for _, ref := range refList {
dps = append(dps, convertDevicePort(ref))
}
return dps
}
func findTurnoutIds(axleCountingMap map[string]*graphicData.AxleCounting, axleIds []string) []string {
if len(axleIds) <= 2 {
return nil
}
turnoutMap := make(map[string]bool)
for _, axleId := range axleIds {
axle := axleCountingMap[axleId]
relTurnoutCount := 0
var turnoutId string
for _, ref := range axle.AxleCountingRef {
if ref.DeviceType == graphicData.RelatedRef_Turnout {
relTurnoutCount++
turnoutId = ref.Id
}
}
if relTurnoutCount == 1 {
turnoutMap[turnoutId] = true
}
}
var turnoutIds []string
for id := range turnoutMap {
turnoutIds = append(turnoutIds, id)
}
return turnoutIds
}
func convertToProtoSignalModel(gSmt graphicData.Signal_Model) proto.Signal_Model {
switch gSmt {
case graphicData.Signal_HLU:
return proto.Signal_HLU
case graphicData.Signal_HL:
return proto.Signal_HL
case graphicData.Signal_HLU_FU:
return proto.Signal_HLU_FU
case graphicData.Signal_HLU_FL:
return proto.Signal_HLU_FL
case graphicData.Signal_AB:
return proto.Signal_AB
case graphicData.Signal_HBU:
return proto.Signal_HBU
default:
panic(fmt.Sprintf("graphicData.Signal_Model[%d]无法映射到proto.Signal_Model", gSmt))
}
}