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 //通号最大制动力是-1.21 //DEFAULT_BRAKE_FORCE = 1.21 //通号最大加速度是0.97 //DEFAULT_TRAIN_TRACTION_ACC = 0.97 //加速度 //DEFAULT_TRAIN_STARTED_SPEED float64 = 80 / 3.6 //列车起步速度单位 米/秒 ) 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) // 映射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) // 车头所在公里标 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 status.BtmBaliseCacheA = &state_proto.TrainBtmCache{BaliseList: make([]*state_proto.BTMState, 3)} status.BtmBaliseCacheB = &state_proto.TrainBtmCache{BaliseList: make([]*state_proto.BTMState, 3)} //初始化列车参数状态 createOrUpdateStateDynamicConfig(status, configTrainData, trainEndsA, trainEndsB) tl := status.TrainLoad * 100 if tl <= 0 { tl = DEFULAT_TRAIN_LOAD * 100 } vobc, _ := initTrainVobc(int64(tl), status.TrainRunUp, status.TrainMaxBrake) status.VobcState = vobc status.Tcc = initTrainTcc(vs) //status.VobcBtm = &state_proto.VobcBtmState{TelegramState: make([]*state_proto.VobcBtmState_TelegramState, 3), History: make(map[uint32]*state_proto.VobcBtmState_VobcBtmHistoryState)} 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 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) connState := tmpTrain.ConnState if connState.Conn { connTypeName := "半实物" if connState.ConnType == state_proto.TrainConnState_PC_SIM && connState.TypeName == ct.TypeName { connTypeName = fmt.Sprintf("车载pc仿真-%v", ct.TypeName) } panic(sys_error.New(fmt.Sprintf("列车[%s]已经连接 [%v],此列车无法连接", k, connTypeName))) return false } return true }) } train.ConnState.Conn = true train.ConnState.ConnType = ct.ConnType train.ConnState.TypeName = ct.TypeName if ct.ConnType == state_proto.TrainConnState_PC_SIM { //train.Tcc.LineInitTimeStamp12 = 0 train.Tcc.LineInitTimeStamp12PortA = 0 train.Tcc.LineInitTimeStamp12PortB = 0 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仿真,无法断开连接")) } defer func() { train.ConnState.Conn = false train.ConnState.ConnType = state_proto.TrainConnState_NONE train.ConnState.TypeName = "" }() } 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, "是否上行", true, "是否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) tailUp, _ := QueryUpAndABByDevice(vs.Repo, tailDeviceId, tailDevicePort, sta.TrainRunUp) if e2 != nil { panic(sys_error.New("动力学传输数据:列车车尾位置计算出错", e2)) } //slog.Debug("车尾位置", tailDeviceId, "偏移", tailDeviceOffset, "所在设备端", tailDevicePort) // 修改world中的列车位置 handleTrainPositionFromDynamic(vs, info, sta, outLinkId, outLinkOffset, tailLinkId, tailLinkOffset) //修改列车激活方向 updateTrainActiveDirFromDynamic(vs, info, sta, id, port, trainHeadActUp) if sta.OldLinkOffset == 0 { sta.OldLinkOffset = outLinkOffset sta.OldLink = outLinkId } if sta.OldTailLinkOffset == 0 { sta.OldTailLinkOffset = tailLinkOffset sta.OldTailLink = tailLinkId } updateTrainBtmPosition(vs, info, sta, outLinkId, outLinkOffset, tailUp, tailLinkId, tailLinkOffset) //slog.Info(fmt.Sprintf("动力学,当前速度(米/秒):%v,加速度:%v,位移距离:%v", info.Speed, info.Acceleration, info.Displacement)) if sta.OldLink != outLinkId { sta.OldLink = outLinkId } if sta.OldLinkOffset != outLinkOffset { sta.OldLinkOffset = outLinkOffset } 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 = info.Displacement //slog.Info(fmt.Sprintf("动力学返回速度:%v,位移:%v,加速度:%v,time:%v", info.Speed, info.Displacement, info.Acceleration, time.Now().UnixMilli())) 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, tailUp bool, tailLinkId string, tailLinkOffset int64) { // 更新BTM中列车位置信息 //isup := sta.TrainActiveDirection == 1 can_btm.Default().HandleTrainHeadPositionInfoForTrain(vs.World, sta, &fi.TrainHeadPositionInfo{ TrainId: sta.Id, Up: info.Up, Link: outLinkId, LinkOffset: outLinkOffset, Speed: info.Speed, Acceleration: info.Acceleration, OldLinkOffset: sta.OldLinkOffset, OldLink: sta.OldLink, TailUp: tailUp, TailLink: tailLinkId, TailLinkOffset: tailLinkOffset, OldTailLink: sta.OldTailLink, OldTailLinkOffset: sta.OldTailLinkOffset, IsLine12: can_btm.IsLine12(sta), }) } // 根据动力学修改列车位置 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 { slog.Warn("列车移除失败,动力学请求失败", err) } thirdConn := train.ConnState if thirdConn.Conn { thirdConn.Conn = false err = TrainPcSimConnOrRemoveHandle(train, false) thirdConn.ConnType = state_proto.TrainConnState_NONE thirdConn.TypeName = "" } if train.VobcState != nil { vobc := train.VobcState vobc.TractionStatus = false vobc.BrakingStatus = true vobc.TractionForce = 0 vobc.BrakeForce = trainBraking(train.TrainLoad, 100, train.TrainMaxBrake) / 1000 * 100 } train.Show = false 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) err := removeTrain(vs, id, t) t.VobcState.VobcBtmInfo = nil //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 }