rts-sim-testing-service/ts/simulation/wayside/memory/wayside_memory_train.go
2024-07-11 14:58:34 +08:00

517 lines
19 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"
"joylink.club/bj-rtsts-server/dto/common_proto"
"joylink.club/bj-rtsts-server/service"
"joylink.club/bj-rtsts-server/third_party/can_btm"
"joylink.club/bj-rtsts-server/third_party/train_pc_sim"
"log/slog"
"reflect"
"strconv"
"time"
"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/rtsssimulation/fi"
"joylink.club/bj-rtsts-server/dto/data_proto"
"joylink.club/bj-rtsts-server/dto/state_proto"
)
const (
DEFULAT_TRAIN_LOAD = 160
DEFAULT_BRAKE_FORCE = 19040
)
func CreateMsgTrainConfig(trainId int, trainLen int64, configTrainData dto.ConfigTrainData) *message.TrainOperationConfig {
return &message.TrainOperationConfig{TrainIndex: trainId, Length: int(trainLen),
DavisParamA: configTrainData.DavisParamA, DavisParamB: configTrainData.DavisParamB,
DavisParamC: configTrainData.DavisParamC, CurveResistanceParamR1: configTrainData.CurveResistanceParamR1,
CurveResistanceParamR2: configTrainData.CurveResistanceParamR2, CurveResistanceParamR3: configTrainData.CurveResistanceParamR3,
CurveResistanceParamR4: configTrainData.CurveResistanceParamR4, RevolvingMassParam: configTrainData.RevolvingMassParam,
Jump: configTrainData.Jump, Slide: configTrainData.Slide,
SlipA: configTrainData.SlipA, SlipR: configTrainData.SlipR, SlipD: int(configTrainData.SlipD),
IdlingA: configTrainData.IdlingA, IdlingR: configTrainData.IdlingR, IdlingD: int(configTrainData.IdlingD),
StopSign: int(configTrainData.StopSign)}
}
// 增加列车状态
func AddTrainStateNew(vs *VerifySimulation, status *state_proto.TrainState, configTrainData dto.ConfigTrainData, trainEndsA dto.ConfigTrainEnds,
trainEndsB dto.ConfigTrainEnds, mapId int32) *sys_error.BusinessError {
allTrainMap := &vs.Memory.Status.TrainStateMap
value, ok := allTrainMap.Load(status.Id)
if ok {
trainState := value.(*state_proto.TrainState)
if trainState.Show {
return sys_error.New(fmt.Sprintf("列车【%s】已存在", status.Id))
}
}
//向动力学发送初始化请求
trainIndex, _ := strconv.ParseUint(status.Id, 10, 16)
slog.Debug("添加列车", "trainIndex", trainIndex, "HeadDeviceId", status.HeadDeviceId, "HeadOffset", status.HeadOffset)
// 映射link、偏移量、运行方向
var uid string
if status.DevicePort == "" {
uid = QueryUidByMidAndComId(mapId, status.HeadDeviceId, &data_proto.Section{})
} else {
uid = QueryUidByMidAndComId(mapId, status.HeadDeviceId, &data_proto.Turnout{})
}
// 车头所在link、link上的偏移
linkId, loffset := QueryLinkAndOffsetByDevice(vs.Repo, uid, status.DevicePort, status.HeadOffset)
// link上的运行方向、设备上的运行方向
up, pointTo := QueryUpAndABByDevice(vs.Repo, uid, status.DevicePort, status.TrainRunUp)
//up, pointTo, _ = QueryDirectionAndABByDevice(vs.Repo, uid, status.DevicePort, status.TrainRunUp)
//fmt.Println(up2, pointTo2)
// 车头所在公里标
kilometer := CalcTrainKilometer(vs.Repo, uid, status.DevicePort, status.TrainRunUp, status.HeadOffset)
// 车尾相对车头link的偏移量
calctailOffset := calcTrailTailOffset(loffset, status.TrainLength, up)
// 车尾位置
tailLink, tailDeviceId, tailDevicePort, tailLOffset, tailDeviceOffset, _, e1 := CalcInitializeLink(vs, linkId, calctailOffset, up)
if e1 != nil {
panic(sys_error.New("添加列车失败,列车车尾占用位置计算出错", e1))
}
status.Up = up
status.DriftTo = pointTo
status.TrainKilometer = kilometer.Value
status.DynamicState = &state_proto.TrainDynamicState{
HeadLinkId: linkId,
HeadLinkOffset: loffset,
TailLinkId: tailLink,
TailLinkOffset: tailLOffset,
RunningUp: status.Up,
//RunningUp: status.TrainRunUp,
}
status.TailDeviceId = vs.GetComIdByUid(tailDeviceId)
status.TailOffset = tailDeviceOffset
status.TailDevicePort = tailDevicePort
//初始化列车参数状态
createOrUpdateStateDynamicConfig(status, configTrainData, trainEndsA, trainEndsB)
tl := configTrainData.TrainLoad
if tl <= 0 {
tl = DEFULAT_TRAIN_LOAD
}
vobc, _ := initTrainVobc(int64(tl), status.TrainRunUp)
status.VobcState = vobc
//status.TrainActiveDirection = trainActDir
status.Tcc = initTrainTcc(vs, status.TrainRunUp, DEFAULT_BRAKE_FORCE)
status.VobcBtm = &state_proto.VobcBtmState{TelegramState: make([]*state_proto.VobcBtmState_TelegramState, 0), History: make(map[uint32]*state_proto.VobcBtmState_VobcBtmHistoryState)}
slog.Debug("列车初始化", "trainIndex", trainIndex, "linkId", linkId, "loffset", loffset)
linkIdInt, _ := strconv.Atoi(linkId)
err := dynamics.Default().RequestAddTrain(&message.InitTrainInfo{
TrainIndex: uint16(trainIndex),
LinkIndex: uint16(linkIdInt),
LinkOffset: uint32(loffset),
Speed: status.Speed / 3.6,
Up: status.Up,
//Up: status.TrainRunUp,
TrainOperationConfig: CreateMsgTrainConfig(int(trainIndex), status.TrainLength, configTrainData),
})
if err != nil {
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()})
}
// world中加车
fi.AddTrainToWorld(vs.World, status.Id)
fi.UpdateTrainPositionFromDynamics(vs.World, fi.TrainPositionInfo{
TrainId: status.Id,
Up: status.Up,
Len: uint32(status.TrainLength),
HeadLink: linkId,
HeadLinkOffset: uint32(loffset),
TailLink: tailLink,
TailLinkOffset: uint32(tailLOffset),
})
// 将信息合并到当前设备状态中
allTrainMap.Store(status.Id, status)
return nil
}
// 列车连接类型更新
func TrainConnTypeUpdate(vs *VerifySimulation, ct *dto.TrainConnThirdDto) {
allTrainMap := &vs.Memory.Status.TrainStateMap
data, ok := allTrainMap.Load(ct.Id)
if !ok {
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", ct.Id)))
}
train := data.(*state_proto.TrainState)
if train.DynamicState.Speed != 0 {
panic(sys_error.New(fmt.Sprintf("列车[%s]需要停稳才能连接半实物", train.Id)))
}
if ct.ConnType != state_proto.TrainConnState_NONE {
//列车连接 半实物或车载pc仿真
allTrainMap.Range(func(k, v any) bool {
tmpTrain := v.(*state_proto.TrainState)
if tmpTrain.ConnState.Conn {
connTypeName := "半实物"
if tmpTrain.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A {
connTypeName = "车载pc仿真-A"
} else if tmpTrain.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B {
connTypeName = "车载pc仿真-B"
}
panic(sys_error.New(fmt.Sprintf("列车[%s]已经连接 [%v],此列车无法连接", k, connTypeName)))
return false
}
return true
})
}
train.ConnState.Conn = true
train.ConnState.ConnType = ct.ConnType
if ct.ConnType == state_proto.TrainConnState_PC_SIM_A || ct.ConnType == state_proto.TrainConnState_PC_SIM_B {
err := TrainPcSimConnOrRemoveHandle(train, true)
if err != nil {
train.ConnState.Conn = false
train.ConnState.ConnType = state_proto.TrainConnState_NONE
panic(sys_error.New(err.Error()))
}
//train_pc_sim.Default().SendDriverActive(train)
}
}
// 列车断开三方连接
func TrainUnConn(vs *VerifySimulation, trainId string) {
allTrainMap := &vs.Memory.Status.TrainStateMap
data, ok := allTrainMap.Load(trainId)
if !ok {
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", trainId)))
}
train := data.(*state_proto.TrainState)
err := TrainPcSimConnOrRemoveHandle(train, false)
if err != nil {
panic(sys_error.New("未连接车载PC仿真无法断开连接"))
}
train.ConnState.Conn = false
train.ConnState.ConnType = state_proto.TrainConnState_NONE
}
func createOrUpdateStateDynamicConfig(trainState *state_proto.TrainState, configTrainData dto.ConfigTrainData, trainEndsA dto.ConfigTrainEnds,
trainEndsB dto.ConfigTrainEnds) {
trainState.TrainDynamicConfig = service.TrainConfigToProtoConvert(&configTrainData)
if trainState.TrainDynamicConfig != nil {
copyTrainEnds(trainState, "TrainEndsA", trainEndsA)
copyTrainEnds(trainState, "TrainEndsB", trainEndsB)
}
}
func copyTrainEnds(trainState *state_proto.TrainState, fieldName string, configData dto.ConfigTrainEnds) {
fieldVal := reflect.ValueOf(trainState).Elem().FieldByName(fieldName)
endsVal := fieldVal.Interface().(*common_proto.TrainEndsState)
if endsVal == nil {
endsVal = &common_proto.TrainEndsState{}
fieldVal.Set(reflect.ValueOf(endsVal))
}
endsVal.SpeedSensorEnableA = configData.SpeedSensorEnableA
endsVal.SpeedSensorEnableB = configData.SpeedSensorEnableB
endsVal.AccOutSpeed = configData.AccOutSpeed
endsVal.RadarEnable = configData.RadarEnable
endsVal.RadarCheckTime = configData.RadarCheckTime
endsVal.RadarCheckSpeedDiff = configData.RadarCheckSpeedDiff
endsVal.RadarOutSpeed = configData.RadarOutSpeed
endsVal.AccEnable = configData.AccEnable
endsVal.AccCheckSpeedDiff = configData.AccCheckSpeedDiff
endsVal.AccCheckTime = configData.AccCheckTime
}
func UpdateConfigTrain(vs *VerifySimulation, ct *dto.ConfigTrainReqDto) {
allTrainMap := &vs.Memory.Status.TrainStateMap
data, ok := allTrainMap.Load(strconv.Itoa(ct.TrainId))
if !ok {
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", ct.TrainId)))
}
trainState, ok := data.(*state_proto.TrainState)
if !ok {
panic(sys_error.New(fmt.Sprintf("列车参数修改断言:列车【%s】不存在", ct.TrainId)))
}
createOrUpdateStateDynamicConfig(trainState, ct.ConfigData, ct.TrainEndsA, ct.TrainEndsB)
trainState.TrainLength = ct.Length
trainState.WheelDiameter = ct.WheelDiameter
requestDynamicConfig(ct)
trainState.TrainEndsA.RadarCheckTimeOverAt = 0
trainState.TrainEndsB.RadarCheckTimeOverAt = 0
trainState.TrainEndsA.AccCheckTimeOverAt = 0
trainState.TrainEndsB.AccCheckTimeOverAt = 0
if ct.TrainEndsA.RadarCheckTime > 0 {
timeAt := time.Now().Add(time.Second * time.Duration(ct.TrainEndsA.RadarCheckTime)).Unix()
trainState.TrainEndsA.RadarCheckTimeOverAt = timeAt
}
if ct.TrainEndsB.RadarCheckTime > 0 {
timeAt := time.Now().Add(time.Second * time.Duration(ct.TrainEndsB.RadarCheckTime)).Unix()
trainState.TrainEndsB.RadarCheckTimeOverAt = timeAt
}
if ct.TrainEndsA.AccCheckTime > 0 {
timeAt := time.Now().Add(time.Second * time.Duration(ct.TrainEndsA.AccCheckTime)).Unix()
trainState.TrainEndsA.AccCheckTimeOverAt = timeAt
}
if ct.TrainEndsB.AccCheckTime > 0 {
timeAt := time.Now().Add(time.Second * time.Duration(ct.TrainEndsB.AccCheckTime)).Unix()
trainState.TrainEndsB.AccCheckTimeOverAt = timeAt
}
}
func requestDynamicConfig(ct *dto.ConfigTrainReqDto) {
msg := CreateMsgTrainConfig(ct.TrainId, ct.Length, ct.ConfigData)
err2 := dynamics.Default().TrainOperationConfig(msg)
err := err2
if err != nil {
slog.Error("列车参数变更请求动力学失败", err)
panic(sys_error.New(fmt.Sprintf("列车参数变更请求动力学失败")))
}
}
// 修改列车信息
func UpdateTrainInfo(vs *VerifySimulation, status *state_proto.TrainState) *state_proto.TrainState {
allTrainMap := &vs.Memory.Status.TrainStateMap
data, ok := allTrainMap.Load(status.Id)
if !ok {
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", status.Id)))
}
sta := data.(*state_proto.TrainState)
sta.TrainLength = status.TrainLength
sta.WheelDiameter = status.WheelDiameter
return sta
}
// 根据动力学发来的信息修改列车状态
func UpdateTrainStateByDynamics(vs *VerifySimulation, trainId string, info *message.DynamicsTrainInfo) *state_proto.TrainState {
data, ok := vs.Memory.Status.TrainStateMap.Load(trainId)
if !ok {
panic(sys_error.New(fmt.Sprintf("动力学传输数据:列车【%s】不存在", trainId)))
}
sta := data.(*state_proto.TrainState)
if !sta.Show {
return sta
}
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)
inLinkId, inLinkOffset := strconv.Itoa(int(info.Link)), int64(info.LinkOffset)
outLinkId, id, port, outLinkOffset, offset, kilometer, e1 := CalcInitializeLink(vs, inLinkId, inLinkOffset, info.Up)
if e1 != nil {
panic(sys_error.New("动力学传输数据:列车车头位置计算出错", e1))
}
//runDirection 指定的是link方向
//pointTO 指的是是否ab或是否到岔心
_, pointTo := QueryDirectionAndABByDevice(vs.Repo, id, port, info.Up)
//slog.Debug("处理动力学转换后的消息", "number", info.Number, "up", info.Up, "Link", info.Link, "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo, "t1Dir:", info.TrainActToMax, "t2Dir:", info.TrainActToMin)
trainHeadActUp := true
if info.TrainActToMax || info.TrainActToMin {
if info.TrainActToMin {
trainHeadActUp = false
}
}
// 车尾相对车头link的偏移量
//calctailOffset := calcTrailTailOffset(outLinkOffset, int64(info.Len), info.Up)
calctailOffset := calcTrailTailOffset(outLinkOffset, int64(info.Len), trainHeadActUp)
tailLinkId, tailDeviceId, tailDevicePort, tailLinkOffset, tailOffset, _, e2 := CalcInitializeLink(vs, outLinkId, calctailOffset, !info.Up)
if e2 != nil {
panic(sys_error.New("动力学传输数据:列车车尾位置计算出错", e2))
}
//slog.Debug("车尾位置", tailDeviceId, "偏移", tailDeviceOffset, "所在设备端", tailDevicePort)
updateTrainBtmPosition(vs, info, sta, outLinkId, outLinkOffset)
// 修改world中的列车位置
handleTrainPositionFromDynamic(vs, info, sta, outLinkId, outLinkOffset, tailLinkId, tailLinkOffset)
//修改列车激活方向
updateTrainActiveDirFromDynamic(vs, info, sta, id, port, trainHeadActUp)
sta.HeadDeviceId = vs.GetComIdByUid(id)
sta.DevicePort = port
sta.HeadOffset = offset
sta.DriftTo = pointTo
sta.TrainKilometer = kilometer.Value
sta.TailDeviceId = vs.GetComIdByUid(tailDeviceId)
sta.TailOffset = tailOffset
sta.TailDevicePort = tailDevicePort
// 赋值动力学信息
sta.DynamicState.Heartbeat = int32(info.LifeSignal)
sta.DynamicState.HeadLinkId = outLinkId
sta.DynamicState.HeadLinkOffset = outLinkOffset
sta.DynamicState.TailLinkId = tailLinkId
sta.DynamicState.TailLinkOffset = tailLinkOffset
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
sta.DynamicState.Displacement = int32(info.Displacement)
pluseCount(sta, info.HeadSpeed1, info.HeadSpeed2, info.TailSpeed1, info.TailSpeed2)
return sta
}
// 根据动力学列车激活端方向,返回前端列车激活端
// 道岔 1是端口到岔心 2是岔心到端口
// 区段 1是a-b 2是b-a
func updateTrainActiveDirFromDynamic(vs *VerifySimulation, info *message.DynamicsTrainInfo, sta *state_proto.TrainState, id, port string, trainHeadActUp bool) {
sta.TrainActiveDirection = 0
if info.TrainActToMax || info.TrainActToMin {
_, pointTo2 := QueryDirectionAndABByDevice(vs.Repo, id, port, trainHeadActUp)
pt := pointTo2
if pt {
sta.TrainActiveDirection = 1
} else {
sta.TrainActiveDirection = 2
}
}
}
// 根据列车位置修改列车应答器
func updateTrainBtmPosition(vs *VerifySimulation, info *message.DynamicsTrainInfo, sta *state_proto.TrainState, outLinkId string, outLinkOffset int64) {
// 更新BTM中列车位置信息
can_btm.Default().HandleTrainHeadPositionInfo(vs.World, sta.VobcBtm, &fi.TrainHeadPositionInfo{
TrainId: sta.Id,
Up: info.Up,
Link: outLinkId,
LinkOffset: outLinkOffset,
Speed: info.Speed,
Acceleration: info.Acceleration,
})
state := can_btm.Default().GetState()
//fmt.Println(state.Telegram)
sta.BtmState = &state
}
// 根据动力学修改列车位置
func handleTrainPositionFromDynamic(vs *VerifySimulation, info *message.DynamicsTrainInfo, sta *state_proto.TrainState, outLinkId string, outLinkOffset int64, tailLinkId string, tailLinkOffset int64) {
fi.UpdateTrainPositionFromDynamics(vs.World, fi.TrainPositionInfo{
TrainId: sta.Id,
Up: info.Up,
Len: info.Len,
HeadLink: outLinkId,
HeadLinkOffset: uint32(outLinkOffset),
TailLink: tailLinkId,
TailLinkOffset: uint32(tailLinkOffset),
})
}
func pluseCount(sta *state_proto.TrainState, h1, h2, t1, t2 float32) {
if sta.PluseCount == nil {
return
}
train_pc_sim.Default().TrainPluseCount(sta, h1, h2, t1, t2)
}
func RemoveAllTrain(vs *VerifySimulation) {
allTrainMap := &vs.Memory.Status.TrainStateMap
if allTrainMap == nil {
slog.Info("当前没有列车不能执行")
return
}
allTrainMap.Range(func(k any, t any) bool {
id := k.(string)
RemoveTrainState(vs, id)
return true
})
}
func removeTrain(vs *VerifySimulation, trainId string, train *state_proto.TrainState) error {
trainIndex, _ := strconv.ParseUint(trainId, 10, 16)
err := dynamics.Default().RequestRemoveTrain(&message.RemoveTrainReq{
TrainIndex: uint16(trainIndex),
})
if err != nil {
return err
}
if train.ConnState.Conn {
train.ConnState.Conn = false
err = TrainPcSimConnOrRemoveHandle(train, false)
if err != nil {
train.ConnState.Conn = true
return err
}
}
if train.VobcState != nil {
vobc := train.VobcState
vobc.TractionStatus = false
vobc.BrakingStatus = true
vobc.TractionForce = 0
vobc.BrakeForce = DEFAULT_BRAKE_FORCE
}
train.Show = false
train.ConnState.ConnType = state_proto.TrainConnState_NONE
return fi.RemoveTrainFromWorld(vs.World, trainId)
}
// 删除列车状态
/*func RemoveTrainState(vs *VerifySimulation, id string) {
allTrainMap := &vs.Memory.Status.TrainStateMap
d, ok := allTrainMap.Load(id)
if ok {
t := d.(*state_proto.TrainState)
trainIndex, _ := strconv.ParseUint(id, 10, 16)
err := dynamics.Default().RequestRemoveTrain(&message.RemoveTrainReq{
TrainIndex: uint16(trainIndex),
})
if err != nil {
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()})
}
// 从仿真内存中移除列车
t.Show = false
// 移除车
fi.RemoveTrainFromWorld(vs.World, id)
allTrainMap.Store(id, t)
} else {
panic(fmt.Sprintf("列车【%s】不存在", id))
}
}*/
func RemoveTrainState(vs *VerifySimulation, id string) {
allTrainMap := &vs.Memory.Status.TrainStateMap
d, ok := allTrainMap.Load(id)
if ok {
t := d.(*state_proto.TrainState)
err := removeTrain(vs, id, t)
clearTrainVobcBtmState(vs, id)
if err != nil {
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()})
}
allTrainMap.Store(id, t)
} else {
panic(fmt.Sprintf("列车【%s】不存在", id))
}
}
func calcTrailTailOffset(headerOffset, length int64, up bool) (calctailOffset int64) {
if up {
calctailOffset = headerOffset - length
} else {
calctailOffset = headerOffset + length
}
return
}
func clearTrainVobcBtmState(vs *VerifySimulation, id string) {
allTrainMap := &vs.Memory.Status.TrainStateMap
d, ok := allTrainMap.Load(id)
if !ok {
slog.Error(fmt.Sprintf("vobc btm 清空操作 列车【%s】不存在", id))
return
}
t := d.(*state_proto.TrainState)
t.VobcBtm.History = nil
}