rts-sim-testing-service/ts/simulation/wayside/memory/wayside_memory_train.go
2024-05-07 15:09:08 +08:00

487 lines
18 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"
"math"
"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))
}
}
//tcc := createTrainControl(vs)
//status.Tcc = tcc
// 显示状态
status.Show = true
//向动力学发送初始化请求
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.RunDirection)
// 车头所在公里标
kilometer := CalcTrainKilometer(vs.Repo, uid, status.DevicePort, status.RunDirection, 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.PointTo = pointTo
status.TrainKilometer = kilometer.Value
status.DynamicState = &state_proto.TrainDynamicState{
HeadLinkId: linkId,
HeadLinkOffset: loffset,
TailLinkId: tailLink,
TailLinkOffset: tailLOffset,
RunningUp: up,
}
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
}
status.VobcState = &state_proto.TrainVobcState{Tc1Active: true, TrainLoad: int64(tl), BrakingStatus: true, BrakeForce: DEFAULT_BRAKE_FORCE, DirectionForward: true}
status.Tcc = initTrainTcc(vs)
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,
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 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 {
connTypeName = "车载pc仿真"
}
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 {
err := TrainPcSimConnOrRemoveHandle(train)
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.ConnState, train.VobcState)
}
}
// 列车断开三方连接
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)
oldType := train.ConnState.ConnType
train.ConnState.Conn = false
train.ConnState.ConnType = state_proto.TrainConnState_NONE
if oldType == state_proto.TrainConnState_PC_SIM {
err := TrainPcSimConnOrRemoveHandle(train)
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)
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, pointTo := QueryDirectionAndABByDevice(vs.Repo, id, port, info.Up)
//slog.Debug("处理动力学转换后的消息", "number", info.Number, "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo)
// 车尾相对车头link的偏移量
calctailOffset := calcTrailTailOffset(outLinkOffset, int64(info.Len), info.Up)
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)
// 更新BTM中列车位置信息
can_btm.Default().HandleTrainHeadPositionInfo(vs.World, &fi.TrainHeadPositionInfo{
TrainId: trainId,
Up: info.Up,
Link: outLinkId,
LinkOffset: outLinkOffset,
Speed: info.Speed,
Acceleration: info.Acceleration,
})
state := can_btm.Default().GetState()
sta.BtmState = &state
// 修改world中的列车位置
fi.UpdateTrainPositionFromDynamics(vs.World, fi.TrainPositionInfo{
TrainId: trainId,
Up: info.Up,
Len: info.Len,
HeadLink: outLinkId,
HeadLinkOffset: uint32(outLinkOffset),
TailLink: tailLinkId,
TailLinkOffset: uint32(tailLinkOffset),
})
sta.HeadDeviceId = vs.GetComIdByUid(id)
sta.DevicePort = port
sta.HeadOffset = offset
sta.PointTo = pointTo
sta.TrainKilometer = kilometer.Value
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.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)
return sta
}
// 接受动力学时间15毫米
const RECEIVE_DYNAMIC_DATA_RATE = 15
func formatSpeedTime(s int32) int32 {
//d3, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", s), 64)
return int32(math.Abs(math.Round(float64(s))))
}
func pluseCount(sta *state_proto.TrainState) {
if sta.PluseCount == nil {
return
}
if sta.RunDirection {
p1 := uint32(formatSpeedTime(sta.DynamicState.HeadSensorSpeed1 * RECEIVE_DYNAMIC_DATA_RATE))
p2 := uint32(formatSpeedTime(sta.DynamicState.HeadSensorSpeed2 * RECEIVE_DYNAMIC_DATA_RATE))
if sta.TrainEndsA.SpeedSensorEnableA {
sta.PluseCount.PulseCount1 = sta.PluseCount.PulseCount1 + p1
}
if sta.TrainEndsA.SpeedSensorEnableB {
sta.PluseCount.PulseCount2 = sta.PluseCount.PulseCount2 + p2
}
} else {
t1 := uint32(formatSpeedTime(sta.DynamicState.TailSensorSpeed1 * RECEIVE_DYNAMIC_DATA_RATE))
t2 := uint32(formatSpeedTime(sta.DynamicState.TailSensorSpeed2 * RECEIVE_DYNAMIC_DATA_RATE))
if sta.TrainEndsB.SpeedSensorEnableA {
sta.PluseCount.PulseCount1 = sta.PluseCount.PulseCount1 + t1
}
if sta.TrainEndsB.SpeedSensorEnableB {
sta.PluseCount.PulseCount2 = sta.PluseCount.PulseCount2 + 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)
if err != nil {
train.ConnState.Conn = true
return err
}
}
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)
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
}