package simulation import ( "encoding/binary" "fmt" "strconv" "sync" "time" "joylink.club/bj-rtsts-server/ats/verify/protos/state" "joylink.club/bj-rtsts-server/ats/verify/simulation/wayside/memory" "joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/third_party/dynamics" "joylink.club/bj-rtsts-server/third_party/semi_physical_train" "joylink.club/bj-rtsts-server/dto" ) // func init() { // // vobc 发来的列车信息 // vobc.RegisterTrainInfoHandler(func(info []byte) { // for _, simulation := range GetSimulationArr() { // simulation.Memory.Status.TrainStateMap.Range(func(_, value any) bool { // train := value.(*state.TrainState) // if !train.Show { // 下线列车 // return false // } // // 拼接列车ID // trainId, err := strconv.Atoi(train.Id) // if err != nil { // panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()}) // } // trainInfo := decoderVobcTrainState(info) // d := append(info, uint8(trainId)) // // 发送给动力学 // dynamics.SendDynamicsTrainMsg(d) // // 存放至列车中 // train.VobcState = trainInfo // return true // }) // } // }) // dynamics.RegisterTrainInfoHandler(func(info *dynamics.TrainInfo) { // for _, simulation := range GetSimulationArr() { // sta, ok := simulation.Memory.Status.TrainStateMap.Load(strconv.Itoa(int(info.Number))) // if !ok { // continue // } // trainState := sta.(*state.TrainState) // // 给半实物仿真发送速度 // vobc.SendTrainSpeedTask(convertVobc(info)) // // 更新列车状态 // memory.UpdateTrainState(simulation, convert(info, trainState, simulation)) // } // }) // } // 仿真存储集合 var simulationMap sync.Map // 创建前检查 func IsExistSimulation() bool { i := 0 simulationMap.Range(func(_, _ any) bool { i++ return true }) return i > 0 } // 创建仿真对象 func CreateSimulation(projectId int32, mapIds []int32) string { simulationId := createSimulationId(projectId) _, e := simulationMap.Load(simulationId) if !e && IsExistSimulation() { panic(dto.ErrorDto{Code: dto.DataAlreadyExist, Message: "已有仿真在运行"}) } if !e { verifySimulation, err := memory.CreateSimulation(projectId, mapIds) if err != nil { panic(fmt.Sprintf("创建仿真失败:%s", err.Error())) } verifySimulation.SimulationId = simulationId if config.Config.Dynamics.Open { // 动力学接口调用 lineBaseInfo := verifySimulation.BuildLineBaseInfo() err := dynamics.Default().RequestStartSimulation(lineBaseInfo) if err != nil { panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()}) } dynamics.Default().Start(verifySimulation) } if config.Config.Vobc.Open { // 半实物系统接口功能启动 semi_physical_train.Default().Start(verifySimulation) } // httpCode, _, err := dynamics.SendSimulationStartReq(lineBaseInfo) // if httpCode != http.StatusOK || err != nil { // panic(dto.ErrorDto{Code: dto.DynamicsError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)}) // } simulationMap.Store(simulationId, verifySimulation) // dynamicsRun(verifySimulation) } return simulationId } // 删除仿真对象 func DestroySimulation(simulationId string) { s, e := simulationMap.Load(simulationId) if !e { return } simulationInfo := s.(*memory.VerifySimulation) simulationMap.Delete(simulationId) // 停止ecs world simulationInfo.World.Close() //ecsSimulation.DestroySimulation(simulationInfo.WorldId) if config.Config.Dynamics.Open { // 停止动力学接口功能 dynamics.Default().Stop() dynamics.Default().RequestStopSimulation() } // //移除道岔状态发送 // dynamics.Stop() // //通知动力学 // httpCode, _, err := dynamics.SendSimulationEndReq() // if httpCode != http.StatusOK { // panic(dto.ErrorDto{Code: dto.DynamicsError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)}) // } } func createSimulationId(projectId int32) string { // 当前服务器IP + 端口 + 项目 return config.SimulationId_prefix + "_" + strconv.Itoa(config.Config.Server.Port) + "_" + strconv.Itoa(int(projectId)) } // 获取仿真列表 func ListAllSimulations() []*dto.SimulationInfoRspDto { var simArr []*dto.SimulationInfoRspDto simulationMap.Range(func(_, v any) bool { s := v.(*memory.VerifySimulation) simArr = append(simArr, &dto.SimulationInfoRspDto{ SimulationId: s.SimulationId, MapId: s.MapIds[0], MapIds: s.MapIds, ProjectId: s.ProjectId, }) return true }) return simArr } // 根据仿真id查找仿真实例 func FindSimulation(simulationId string) *memory.VerifySimulation { m, e := simulationMap.Load(simulationId) if e { return m.(*memory.VerifySimulation) } return nil } // 获取普通仿真数组 func GetSimulationArr() []*memory.VerifySimulation { var result []*memory.VerifySimulation simulationMap.Range(func(_, v any) bool { result = append(result, v.(*memory.VerifySimulation)) return true }) return result } /* func convert(info *dynamics.TrainInfo, sta *state.TrainState, simulation *memory.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 := memory.QueryDeviceByCalcLink(simulation.Repo, strconv.Itoa(int(info.Link)), int64(info.LinkOffset), info.Up) slog.Debug("处理动力学转换后的消息", "number", info.Number, "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo) 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 convert(info *dynamics.TrainInfo, sta *state.TrainState, simulation *memory.VerifySimulation) *state.TrainState { // if sta.VobcState.LifeSignal == int32(info.VobcLifeSignal) { // sta.ControlDelayTime = time.Now().UnixMilli() - sta.VobcState.UpdateTime // if sta.ControlDelayTime > memory.DelayMaxTime { // sta.ControlDelayTime = memory.DelayMaxTime // } // } else { // sta.ControlDelayTime = memory.DelayMaxTime // } // slog.Debug("收到动力学原始消息", "Number", info.Number, "Link", info.Link, "LinkOffset", info.LinkOffset) // id, port, offset, runDirection, pointTo, kilometer := memory.QueryDeviceByCalcLink(simulation.Repo, strconv.Itoa(int(info.Link)), int64(info.LinkOffset), info.Up) // slog.Debug("处理动力学转换后的消息", "number", info.Number, // "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo) // 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 // } // // 转换成Vobc发送参数 // func convertVobc(info *dynamics.TrainInfo) *vobc.SendTrainInfo { // param := &vobc.SendTrainInfo{ // Speed: uint16(math.Abs(float64(info.Speed * 36))), // Upslope: info.UpSlope, // Slope: uint16(info.Slope), // TotalResistance: uint32(math.Abs(float64(info.TotalResistance / 10))), // AirResistance: uint32(info.AirResistance / 10), // SlopeResistance: uint32(math.Abs(float64(info.SlopeResistance / 10))), // CurveResistance: uint32(info.CurveResistance / 10), // } // d := math.Abs(float64(info.Acceleration * 100)) // if info.Acceleration > 0 { // param.Acceleration = uint8(d) // } else { // param.Deceleration = uint8(d) // } // return param // } // func convert(info *dynamics.TrainInfo, sta *state.TrainState, simulation *memory.VerifySimulation) *state.TrainState { // slog.Debug("收到动力学原始消息", "Number", info.Number, "Link", info.Link, "LinkOffset", info.LinkOffset) // id, port, offset, runDirection, pointTo, kilometer := memory.QueryDeviceByCalcLink(simulation.Repo, strconv.Itoa(int(info.Link)), int64(info.LinkOffset), info.Up) // slog.Debug("处理动力学转换后的消息", "number", info.Number, // "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo) // 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 dynamicsRun(verifySimulation *memory.VerifySimulation) { // _ = dynamics.Run(func() []*dynamics.TurnoutInfo { // stateSlice := memory.GetAllTurnoutState(verifySimulation) // var turnoutInfoSlice []*dynamics.TurnoutInfo // for _, sta := range stateSlice { // code64, err := strconv.ParseUint(sta.Id, 10, 16) // if err != nil { // slog.Error("id转uint16报错", err) // } // info := dynamics.TurnoutInfo{ // Code: uint16(code64), // NPosition: sta.Dw, // RPosition: sta.Fw, // } // turnoutInfoSlice = append(turnoutInfoSlice, &info) // } // return turnoutInfoSlice // }) // } // func buildLineBaseInfo(sim *memory.VerifySimulation) *dynamics.LineBaseInfo { // info := &dynamics.LineBaseInfo{} // for _, model := range sim.Repo.LinkList() { // id, _ := strconv.Atoi(model.Id()) // link := &dynamics.Link{ // ID: int32(id), // Len: int32(model.Length()), // } // info.LinkList = append(info.LinkList, link) // if model.ARelation() != nil { // turnoutId, _ := strconv.Atoi(sim.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(sim.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 sim.Repo.SlopeList() { // id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id())) // slope := &dynamics.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 sim.Repo.SectionalCurvatureList() { // id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id())) // curve := &dynamics.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 // } // 解析VOBC列车信息 func decoderVobcTrainState(buf []byte) *state.TrainVobcState { trainVobcInfo := &state.TrainVobcState{} trainVobcInfo.LifeSignal = int32(binary.BigEndian.Uint16(buf[0:2])) b2 := buf[2] trainVobcInfo.Tc1Active = (b2 & 1) != 0 trainVobcInfo.Tc2Active = (b2 & (1 << 1)) != 0 trainVobcInfo.DirectionForward = (b2 & (1 << 2)) != 0 trainVobcInfo.DirectionBackward = (b2 & (1 << 3)) != 0 trainVobcInfo.TractionStatus = (b2 & (1 << 4)) != 0 trainVobcInfo.BrakingStatus = (b2 & (1 << 5)) != 0 trainVobcInfo.EmergencyBrakingStatus = (b2 & (1 << 6)) != 0 trainVobcInfo.TurnbackStatus = (b2 & 7) != 0 b3 := buf[3] trainVobcInfo.JumpStatus = (b3 & 1) != 0 trainVobcInfo.Ato = (b3 & (1 << 1)) != 0 trainVobcInfo.Fam = (b3 & (1 << 2)) != 0 trainVobcInfo.Cam = (b3 & (1 << 3)) != 0 trainVobcInfo.TractionSafetyCircuit = (b3 & (1 << 4)) != 0 trainVobcInfo.ParkingBrakeStatus = (b3 & (1 << 5)) != 0 trainVobcInfo.MaintainBrakeStatus = (b3 & (1 << 6)) != 0 trainVobcInfo.TractionForce = int64(binary.BigEndian.Uint16(buf[4:6])) trainVobcInfo.BrakeForce = int64(binary.BigEndian.Uint16(buf[6:8])) trainVobcInfo.TrainLoad = int64(binary.BigEndian.Uint16(buf[8:10])) b4 := buf[15] trainVobcInfo.LeftDoorOpenCommand = (b4 & 1) != 0 trainVobcInfo.RightDoorOpenCommand = (b4 & (1 << 1)) != 0 trainVobcInfo.LeftDoorCloseCommand = (b4 & (1 << 2)) != 0 trainVobcInfo.RightDoorCloseCommand = (b4 & (1 << 3)) != 0 trainVobcInfo.AllDoorClose = (b4 & (1 << 4)) != 0 trainVobcInfo.UpdateTime = time.Now().UnixMilli() return trainVobcInfo } // // 解析VOBC列车信息 // func decoderVobcTrainState(buf []byte) *state.TrainVobcState { // trainVobcInfo := &state.TrainVobcState{} // trainVobcInfo.LifeSignal = int32(binary.BigEndian.Uint16(buf[0:2])) // b2 := buf[2] // trainVobcInfo.Tc1Active = (b2 & 1) != 0 // trainVobcInfo.Tc2Active = (b2 & (1 << 1)) != 0 // trainVobcInfo.DirectionForward = (b2 & (1 << 2)) != 0 // trainVobcInfo.DirectionBackward = (b2 & (1 << 3)) != 0 // trainVobcInfo.TractionStatus = (b2 & (1 << 4)) != 0 // trainVobcInfo.BrakingStatus = (b2 & (1 << 5)) != 0 // trainVobcInfo.EmergencyBrakingStatus = (b2 & (1 << 6)) != 0 // trainVobcInfo.TurnbackStatus = (b2 & 7) != 0 // b3 := buf[3] // trainVobcInfo.JumpStatus = (b3 & 1) != 0 // trainVobcInfo.Ato = (b3 & (1 << 1)) != 0 // trainVobcInfo.Fam = (b3 & (1 << 2)) != 0 // trainVobcInfo.Cam = (b3 & (1 << 3)) != 0 // trainVobcInfo.TractionSafetyCircuit = (b3 & (1 << 4)) != 0 // trainVobcInfo.ParkingBrakeStatus = (b3 & (1 << 5)) != 0 // trainVobcInfo.MaintainBrakeStatus = (b3 & (1 << 6)) != 0 // trainVobcInfo.TractionForce = int64(binary.BigEndian.Uint16(buf[4:6])) // trainVobcInfo.BrakeForce = int64(binary.BigEndian.Uint16(buf[6:8])) // trainVobcInfo.TrainLoad = int64(binary.BigEndian.Uint16(buf[8:10])) // b4 := buf[15] // trainVobcInfo.LeftDoorOpenCommand = (b4 & 1) != 0 // trainVobcInfo.RightDoorOpenCommand = (b4 & (1 << 1)) != 0 // trainVobcInfo.LeftDoorCloseCommand = (b4 & (1 << 2)) != 0 // trainVobcInfo.RightDoorCloseCommand = (b4 & (1 << 3)) != 0 // trainVobcInfo.AllDoorClose = (b4 & (1 << 4)) != 0 // return trainVobcInfo // } // // 发送给前端的速度格式化 // func speedParse(speed float32) int32 { // return int32(math.Abs(float64(speed * 3.6 * 100))) // }