重构动力学、半实物接口(结构完成,实现未完)
This commit is contained in:
parent
eb71e7cd73
commit
81724bd707
|
@ -242,7 +242,7 @@ func switchOperation(c *gin.Context) {
|
||||||
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
||||||
}
|
}
|
||||||
simulation := checkDeviceDataAndReturn(req.SimulationId)
|
simulation := checkDeviceDataAndReturn(req.SimulationId)
|
||||||
slog.Info("传入状态参数", req)
|
slog.Info("传入状态参数", "request", req)
|
||||||
memory.HandleTurnoutOperation(simulation, req)
|
memory.HandleTurnoutOperation(simulation, req)
|
||||||
// memory.ChangeTurnoutState(simulation, &state.SwitchState{
|
// memory.ChangeTurnoutState(simulation, &state.SwitchState{
|
||||||
// Id: req.DeviceId,
|
// Id: req.DeviceId,
|
||||||
|
|
|
@ -1,68 +1,59 @@
|
||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"log/slog"
|
|
||||||
"math"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"joylink.club/rtsssimulation/repository/model/proto"
|
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"joylink.club/bj-rtsts-server/ats/verify/protos/state"
|
|
||||||
"joylink.club/bj-rtsts-server/ats/verify/simulation/wayside/memory"
|
"joylink.club/bj-rtsts-server/ats/verify/simulation/wayside/memory"
|
||||||
"joylink.club/bj-rtsts-server/config"
|
"joylink.club/bj-rtsts-server/config"
|
||||||
"joylink.club/bj-rtsts-server/dynamics"
|
"joylink.club/bj-rtsts-server/third_party/dynamics"
|
||||||
"joylink.club/bj-rtsts-server/vobc"
|
"joylink.club/bj-rtsts-server/third_party/semi_physical_train"
|
||||||
|
|
||||||
"joylink.club/bj-rtsts-server/dto"
|
"joylink.club/bj-rtsts-server/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
// func init() {
|
||||||
// vobc 发来的列车信息
|
// // vobc 发来的列车信息
|
||||||
vobc.RegisterTrainInfoHandler(func(info []byte) {
|
// vobc.RegisterTrainInfoHandler(func(info []byte) {
|
||||||
memory.UdpUpdateTime.VobcTime = time.Now().UnixMilli()
|
// memory.UdpUpdateTime.VobcTime = time.Now().UnixMilli()
|
||||||
for _, simulation := range GetSimulationArr() {
|
// for _, simulation := range GetSimulationArr() {
|
||||||
simulation.Memory.Status.TrainStateMap.Range(func(_, value any) bool {
|
// simulation.Memory.Status.TrainStateMap.Range(func(_, value any) bool {
|
||||||
train := value.(*state.TrainState)
|
// train := value.(*state.TrainState)
|
||||||
if !train.Show { // 下线列车
|
// if !train.Show { // 下线列车
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
// 拼接列车ID
|
// // 拼接列车ID
|
||||||
trainId, err := strconv.Atoi(train.Id)
|
// trainId, err := strconv.Atoi(train.Id)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
// panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
||||||
}
|
// }
|
||||||
trainInfo := decoderVobcTrainState(info)
|
// trainInfo := decoderVobcTrainState(info)
|
||||||
d := append(info, uint8(trainId))
|
// d := append(info, uint8(trainId))
|
||||||
// 发送给动力学
|
// // 发送给动力学
|
||||||
dynamics.SendDynamicsTrainMsg(d)
|
// dynamics.SendDynamicsTrainMsg(d)
|
||||||
// 存放至列车中
|
// // 存放至列车中
|
||||||
train.VobcState = trainInfo
|
// train.VobcState = trainInfo
|
||||||
return true
|
// return true
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
dynamics.RegisterTrainInfoHandler(func(info *dynamics.TrainInfo) {
|
// dynamics.RegisterTrainInfoHandler(func(info *dynamics.TrainInfo) {
|
||||||
slog.Info("发送到vobc的列车编号为", info.Number)
|
// slog.Info("发送到vobc的列车编号为", info.Number)
|
||||||
memory.UdpUpdateTime.DynamicsTime = time.Now().UnixMilli()
|
// memory.UdpUpdateTime.DynamicsTime = time.Now().UnixMilli()
|
||||||
for _, simulation := range GetSimulationArr() {
|
// for _, simulation := range GetSimulationArr() {
|
||||||
sta, ok := simulation.Memory.Status.TrainStateMap.Load(strconv.Itoa(int(info.Number)))
|
// sta, ok := simulation.Memory.Status.TrainStateMap.Load(strconv.Itoa(int(info.Number)))
|
||||||
if !ok {
|
// if !ok {
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
trainState := sta.(*state.TrainState)
|
// trainState := sta.(*state.TrainState)
|
||||||
// 给半实物仿真发送速度
|
// // 给半实物仿真发送速度
|
||||||
vobc.SendTrainSpeedTask(math.Abs(float64(info.Speed * 36)))
|
// vobc.SendTrainSpeedTask(math.Abs(float64(info.Speed * 36)))
|
||||||
// 更新列车状态
|
// // 更新列车状态
|
||||||
memory.UpdateTrainState(simulation, convert(info, trainState, simulation))
|
// memory.UpdateTrainState(simulation, convert(info, trainState, simulation))
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 仿真存储集合
|
// 仿真存储集合
|
||||||
var simulationMap sync.Map
|
var simulationMap sync.Map
|
||||||
|
@ -90,14 +81,25 @@ func CreateSimulation(projectId int32, mapIds []int32) string {
|
||||||
panic(fmt.Sprintf("创建仿真失败:%s", err.Error()))
|
panic(fmt.Sprintf("创建仿真失败:%s", err.Error()))
|
||||||
}
|
}
|
||||||
verifySimulation.SimulationId = simulationId
|
verifySimulation.SimulationId = simulationId
|
||||||
//通知动力学
|
if config.Config.Dynamics.Open {
|
||||||
lineBaseInfo := buildLineBaseInfo(verifySimulation)
|
// 动力学接口调用
|
||||||
httpCode, _, err := dynamics.SendSimulationStartReq(lineBaseInfo)
|
lineBaseInfo := verifySimulation.BuildLineBaseInfo()
|
||||||
if httpCode != http.StatusOK || err != nil {
|
err := dynamics.Default().RequestStartSimulation(lineBaseInfo)
|
||||||
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)})
|
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)
|
simulationMap.Store(simulationId, verifySimulation)
|
||||||
dynamicsRun(verifySimulation)
|
// dynamicsRun(verifySimulation)
|
||||||
}
|
}
|
||||||
return simulationId
|
return simulationId
|
||||||
}
|
}
|
||||||
|
@ -113,13 +115,18 @@ func DestroySimulation(simulationId string) {
|
||||||
// 停止ecs world
|
// 停止ecs world
|
||||||
simulationInfo.World.Close()
|
simulationInfo.World.Close()
|
||||||
//ecsSimulation.DestroySimulation(simulationInfo.WorldId)
|
//ecsSimulation.DestroySimulation(simulationInfo.WorldId)
|
||||||
//移除道岔状态发送
|
if config.Config.Dynamics.Open {
|
||||||
dynamics.Stop()
|
// 停止动力学接口功能
|
||||||
//通知动力学
|
dynamics.Default().Stop()
|
||||||
httpCode, _, err := dynamics.SendSimulationEndReq()
|
dynamics.Default().RequestStopSimulation()
|
||||||
if httpCode != http.StatusOK {
|
|
||||||
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)})
|
|
||||||
}
|
}
|
||||||
|
// //移除道岔状态发送
|
||||||
|
// 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 {
|
func createSimulationId(projectId int32) string {
|
||||||
|
@ -162,170 +169,170 @@ func GetSimulationArr() []*memory.VerifySimulation {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func convert(info *dynamics.TrainInfo, sta *state.TrainState, simulation *memory.VerifySimulation) *state.TrainState {
|
// func convert(info *dynamics.TrainInfo, sta *state.TrainState, simulation *memory.VerifySimulation) *state.TrainState {
|
||||||
slog.Debug("收到动力学原始消息", "Number", info.Number, "Link", info.Link, "LinkOffset", info.LinkOffset)
|
// 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)
|
// 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,
|
// slog.Debug("处理动力学转换后的消息", "number", info.Number,
|
||||||
"车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo)
|
// "车头位置", id, "偏移", offset, "是否上行", runDirection, "是否ab", pointTo)
|
||||||
sta.HeadDeviceId = simulation.GetComIdByUid(id)
|
// sta.HeadDeviceId = simulation.GetComIdByUid(id)
|
||||||
sta.DevicePort = port
|
// sta.DevicePort = port
|
||||||
sta.HeadOffset = offset
|
// sta.HeadOffset = offset
|
||||||
sta.PointTo = pointTo
|
// sta.PointTo = pointTo
|
||||||
sta.TrainKilometer = kilometer
|
// sta.TrainKilometer = kilometer
|
||||||
sta.RunDirection = runDirection
|
// sta.RunDirection = runDirection
|
||||||
//判定车头方向
|
// //判定车头方向
|
||||||
sta.HeadDirection = runDirection
|
// sta.HeadDirection = runDirection
|
||||||
if sta.VobcState != nil {
|
// if sta.VobcState != nil {
|
||||||
if sta.VobcState.DirectionForward {
|
// if sta.VobcState.DirectionForward {
|
||||||
sta.HeadDirection = runDirection
|
// sta.HeadDirection = runDirection
|
||||||
} else if sta.VobcState.DirectionBackward {
|
// } else if sta.VobcState.DirectionBackward {
|
||||||
sta.HeadDirection = !runDirection
|
// sta.HeadDirection = !runDirection
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if info.Speed < 0 {
|
// if info.Speed < 0 {
|
||||||
sta.RunDirection = !sta.RunDirection
|
// sta.RunDirection = !sta.RunDirection
|
||||||
}
|
// }
|
||||||
// 赋值动力学信息
|
// // 赋值动力学信息
|
||||||
sta.DynamicState.Heartbeat = int32(info.LifeSignal)
|
// sta.DynamicState.Heartbeat = int32(info.LifeSignal)
|
||||||
sta.DynamicState.HeadLinkId = strconv.Itoa(int(info.Link))
|
// sta.DynamicState.HeadLinkId = strconv.Itoa(int(info.Link))
|
||||||
sta.DynamicState.HeadLinkOffset = int64(info.LinkOffset)
|
// sta.DynamicState.HeadLinkOffset = int64(info.LinkOffset)
|
||||||
sta.DynamicState.Slope = int32(info.Slope)
|
// sta.DynamicState.Slope = int32(info.Slope)
|
||||||
sta.DynamicState.Upslope = info.UpSlope
|
// sta.DynamicState.Upslope = info.UpSlope
|
||||||
sta.DynamicState.RunningUp = info.Up
|
// sta.DynamicState.RunningUp = info.Up
|
||||||
sta.DynamicState.RunningResistanceSum = float32(info.TotalResistance) / 1000
|
// sta.DynamicState.RunningResistanceSum = float32(info.TotalResistance) / 1000
|
||||||
sta.DynamicState.AirResistance = float32(info.AirResistance) / 1000
|
// sta.DynamicState.AirResistance = float32(info.AirResistance) / 1000
|
||||||
sta.DynamicState.RampResistance = float32(info.SlopeResistance) / 1000
|
// sta.DynamicState.RampResistance = float32(info.SlopeResistance) / 1000
|
||||||
sta.DynamicState.CurveResistance = float32(info.CurveResistance) / 1000
|
// sta.DynamicState.CurveResistance = float32(info.CurveResistance) / 1000
|
||||||
sta.DynamicState.Speed = speedParse(info.Speed)
|
// sta.DynamicState.Speed = speedParse(info.Speed)
|
||||||
sta.DynamicState.HeadSensorSpeed1 = speedParse(info.HeadSpeed1)
|
// sta.DynamicState.HeadSensorSpeed1 = speedParse(info.HeadSpeed1)
|
||||||
sta.DynamicState.HeadSensorSpeed2 = speedParse(info.HeadSpeed2)
|
// sta.DynamicState.HeadSensorSpeed2 = speedParse(info.HeadSpeed2)
|
||||||
sta.DynamicState.TailSensorSpeed1 = speedParse(info.TailSpeed1)
|
// sta.DynamicState.TailSensorSpeed1 = speedParse(info.TailSpeed1)
|
||||||
sta.DynamicState.TailSensorSpeed2 = speedParse(info.TailSpeed2)
|
// sta.DynamicState.TailSensorSpeed2 = speedParse(info.TailSpeed2)
|
||||||
sta.DynamicState.HeadRadarSpeed = speedParse(info.HeadRadarSpeed)
|
// sta.DynamicState.HeadRadarSpeed = speedParse(info.HeadRadarSpeed)
|
||||||
sta.DynamicState.TailRadarSpeed = speedParse(info.TailRadarSpeed)
|
// sta.DynamicState.TailRadarSpeed = speedParse(info.TailRadarSpeed)
|
||||||
sta.DynamicState.Acceleration = info.Acceleration
|
// sta.DynamicState.Acceleration = info.Acceleration
|
||||||
return sta
|
// return sta
|
||||||
}
|
// }
|
||||||
|
|
||||||
func dynamicsRun(verifySimulation *memory.VerifySimulation) {
|
// func dynamicsRun(verifySimulation *memory.VerifySimulation) {
|
||||||
_ = dynamics.Run(func() []*dynamics.TurnoutInfo {
|
// _ = dynamics.Run(func() []*dynamics.TurnoutInfo {
|
||||||
stateSlice := memory.GetAllTurnoutState(verifySimulation)
|
// stateSlice := memory.GetAllTurnoutState(verifySimulation)
|
||||||
var turnoutInfoSlice []*dynamics.TurnoutInfo
|
// var turnoutInfoSlice []*dynamics.TurnoutInfo
|
||||||
for _, sta := range stateSlice {
|
// for _, sta := range stateSlice {
|
||||||
code64, err := strconv.ParseUint(sta.Id, 10, 16)
|
// code64, err := strconv.ParseUint(sta.Id, 10, 16)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
slog.Error("id转uint16报错", err)
|
// slog.Error("id转uint16报错", err)
|
||||||
}
|
// }
|
||||||
info := dynamics.TurnoutInfo{
|
// info := dynamics.TurnoutInfo{
|
||||||
Code: uint16(code64),
|
// Code: uint16(code64),
|
||||||
NPosition: sta.Dw,
|
// NPosition: sta.Dw,
|
||||||
RPosition: sta.Fw,
|
// RPosition: sta.Fw,
|
||||||
}
|
// }
|
||||||
turnoutInfoSlice = append(turnoutInfoSlice, &info)
|
// turnoutInfoSlice = append(turnoutInfoSlice, &info)
|
||||||
}
|
// }
|
||||||
return turnoutInfoSlice
|
// return turnoutInfoSlice
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
func buildLineBaseInfo(sim *memory.VerifySimulation) *dynamics.LineBaseInfo {
|
// func buildLineBaseInfo(sim *memory.VerifySimulation) *dynamics.LineBaseInfo {
|
||||||
info := &dynamics.LineBaseInfo{}
|
// info := &dynamics.LineBaseInfo{}
|
||||||
for _, model := range sim.Repo.LinkList() {
|
// for _, model := range sim.Repo.LinkList() {
|
||||||
id, _ := strconv.Atoi(model.Id())
|
// id, _ := strconv.Atoi(model.Id())
|
||||||
link := &dynamics.Link{
|
// link := &dynamics.Link{
|
||||||
ID: int32(id),
|
// ID: int32(id),
|
||||||
Len: int32(model.Length()),
|
// Len: int32(model.Length()),
|
||||||
}
|
// }
|
||||||
info.LinkList = append(info.LinkList, link)
|
// info.LinkList = append(info.LinkList, link)
|
||||||
if model.ARelation() != nil {
|
// if model.ARelation() != nil {
|
||||||
turnoutId, _ := strconv.Atoi(sim.GetComIdByUid(model.ARelation().Device().Id()))
|
// turnoutId, _ := strconv.Atoi(sim.GetComIdByUid(model.ARelation().Device().Id()))
|
||||||
link.ARelTurnoutId = int32(turnoutId)
|
// link.ARelTurnoutId = int32(turnoutId)
|
||||||
switch model.ARelation().Port() {
|
// switch model.ARelation().Port() {
|
||||||
case proto.Port_A:
|
// case proto.Port_A:
|
||||||
link.ARelTurnoutPoint = "A"
|
// link.ARelTurnoutPoint = "A"
|
||||||
case proto.Port_B:
|
// case proto.Port_B:
|
||||||
link.ARelTurnoutPoint = "B"
|
// link.ARelTurnoutPoint = "B"
|
||||||
case proto.Port_C:
|
// case proto.Port_C:
|
||||||
link.ARelTurnoutPoint = "C"
|
// link.ARelTurnoutPoint = "C"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if model.BRelation() != nil {
|
// if model.BRelation() != nil {
|
||||||
turnoutId, _ := strconv.Atoi(sim.GetComIdByUid(model.BRelation().Device().Id()))
|
// turnoutId, _ := strconv.Atoi(sim.GetComIdByUid(model.BRelation().Device().Id()))
|
||||||
link.BRelTurnoutId = int32(turnoutId)
|
// link.BRelTurnoutId = int32(turnoutId)
|
||||||
switch model.BRelation().Port() {
|
// switch model.BRelation().Port() {
|
||||||
case proto.Port_A:
|
// case proto.Port_A:
|
||||||
link.BRelTurnoutPoint = "A"
|
// link.BRelTurnoutPoint = "A"
|
||||||
case proto.Port_B:
|
// case proto.Port_B:
|
||||||
link.BRelTurnoutPoint = "B"
|
// link.BRelTurnoutPoint = "B"
|
||||||
case proto.Port_C:
|
// case proto.Port_C:
|
||||||
link.BRelTurnoutPoint = "C"
|
// link.BRelTurnoutPoint = "C"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
for _, model := range sim.Repo.SlopeList() {
|
// for _, model := range sim.Repo.SlopeList() {
|
||||||
id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id()))
|
// id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id()))
|
||||||
slope := &dynamics.Slope{
|
// slope := &dynamics.Slope{
|
||||||
ID: int32(id),
|
// ID: int32(id),
|
||||||
StartLinkOffset: int32(model.StartLinkPosition().Offset()),
|
// StartLinkOffset: int32(model.StartLinkPosition().Offset()),
|
||||||
EndLinkOffset: int32(model.EndLinkPosition().Offset()),
|
// EndLinkOffset: int32(model.EndLinkPosition().Offset()),
|
||||||
DegreeTrig: model.Degree(),
|
// DegreeTrig: model.Degree(),
|
||||||
}
|
// }
|
||||||
info.SlopeList = append(info.SlopeList, slope)
|
// info.SlopeList = append(info.SlopeList, slope)
|
||||||
startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id())
|
// startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id())
|
||||||
slope.StartLinkId = int32(startLinkId)
|
// slope.StartLinkId = int32(startLinkId)
|
||||||
endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id())
|
// endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id())
|
||||||
slope.EndLinkId = int32(endLinkId)
|
// slope.EndLinkId = int32(endLinkId)
|
||||||
}
|
// }
|
||||||
for _, model := range sim.Repo.SectionalCurvatureList() {
|
// for _, model := range sim.Repo.SectionalCurvatureList() {
|
||||||
id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id()))
|
// id, _ := strconv.Atoi(sim.GetComIdByUid(model.Id()))
|
||||||
curve := &dynamics.Curve{
|
// curve := &dynamics.Curve{
|
||||||
ID: int32(id),
|
// ID: int32(id),
|
||||||
StartLinkOffset: int32(model.StartLinkPosition().Offset()),
|
// StartLinkOffset: int32(model.StartLinkPosition().Offset()),
|
||||||
EndLinkOffset: int32(model.EndLinkPosition().Offset()),
|
// EndLinkOffset: int32(model.EndLinkPosition().Offset()),
|
||||||
Curvature: model.Radius(),
|
// Curvature: model.Radius(),
|
||||||
}
|
// }
|
||||||
info.CurveList = append(info.CurveList, curve)
|
// info.CurveList = append(info.CurveList, curve)
|
||||||
startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id())
|
// startLinkId, _ := strconv.Atoi(model.StartLinkPosition().Link().Id())
|
||||||
curve.StartLinkId = int32(startLinkId)
|
// curve.StartLinkId = int32(startLinkId)
|
||||||
endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id())
|
// endLinkId, _ := strconv.Atoi(model.EndLinkPosition().Link().Id())
|
||||||
curve.EndLinkId = int32(endLinkId)
|
// curve.EndLinkId = int32(endLinkId)
|
||||||
}
|
// }
|
||||||
return info
|
// return info
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 解析VOBC列车信息
|
// // 解析VOBC列车信息
|
||||||
func decoderVobcTrainState(buf []byte) *state.TrainVobcState {
|
// func decoderVobcTrainState(buf []byte) *state.TrainVobcState {
|
||||||
trainVobcInfo := &state.TrainVobcState{}
|
// trainVobcInfo := &state.TrainVobcState{}
|
||||||
trainVobcInfo.LifeSignal = int32(binary.BigEndian.Uint16(buf[0:2]))
|
// trainVobcInfo.LifeSignal = int32(binary.BigEndian.Uint16(buf[0:2]))
|
||||||
b2 := buf[2]
|
// b2 := buf[2]
|
||||||
trainVobcInfo.Tc1Active = (b2 & 1) != 0
|
// trainVobcInfo.Tc1Active = (b2 & 1) != 0
|
||||||
trainVobcInfo.Tc2Active = (b2 & (1 << 1)) != 0
|
// trainVobcInfo.Tc2Active = (b2 & (1 << 1)) != 0
|
||||||
trainVobcInfo.DirectionForward = (b2 & (1 << 2)) != 0
|
// trainVobcInfo.DirectionForward = (b2 & (1 << 2)) != 0
|
||||||
trainVobcInfo.DirectionBackward = (b2 & (1 << 3)) != 0
|
// trainVobcInfo.DirectionBackward = (b2 & (1 << 3)) != 0
|
||||||
trainVobcInfo.TractionStatus = (b2 & (1 << 4)) != 0
|
// trainVobcInfo.TractionStatus = (b2 & (1 << 4)) != 0
|
||||||
trainVobcInfo.BrakingStatus = (b2 & (1 << 5)) != 0
|
// trainVobcInfo.BrakingStatus = (b2 & (1 << 5)) != 0
|
||||||
trainVobcInfo.EmergencyBrakingStatus = (b2 & (1 << 6)) != 0
|
// trainVobcInfo.EmergencyBrakingStatus = (b2 & (1 << 6)) != 0
|
||||||
trainVobcInfo.TurnbackStatus = (b2 & 7) != 0
|
// trainVobcInfo.TurnbackStatus = (b2 & 7) != 0
|
||||||
b3 := buf[3]
|
// b3 := buf[3]
|
||||||
trainVobcInfo.JumpStatus = (b3 & 1) != 0
|
// trainVobcInfo.JumpStatus = (b3 & 1) != 0
|
||||||
trainVobcInfo.Ato = (b3 & (1 << 1)) != 0
|
// trainVobcInfo.Ato = (b3 & (1 << 1)) != 0
|
||||||
trainVobcInfo.Fam = (b3 & (1 << 2)) != 0
|
// trainVobcInfo.Fam = (b3 & (1 << 2)) != 0
|
||||||
trainVobcInfo.Cam = (b3 & (1 << 3)) != 0
|
// trainVobcInfo.Cam = (b3 & (1 << 3)) != 0
|
||||||
trainVobcInfo.TractionSafetyCircuit = (b3 & (1 << 4)) != 0
|
// trainVobcInfo.TractionSafetyCircuit = (b3 & (1 << 4)) != 0
|
||||||
trainVobcInfo.ParkingBrakeStatus = (b3 & (1 << 5)) != 0
|
// trainVobcInfo.ParkingBrakeStatus = (b3 & (1 << 5)) != 0
|
||||||
trainVobcInfo.MaintainBrakeStatus = (b3 & (1 << 6)) != 0
|
// trainVobcInfo.MaintainBrakeStatus = (b3 & (1 << 6)) != 0
|
||||||
trainVobcInfo.TractionForce = int64(binary.BigEndian.Uint16(buf[4:6]))
|
// trainVobcInfo.TractionForce = int64(binary.BigEndian.Uint16(buf[4:6]))
|
||||||
trainVobcInfo.BrakeForce = int64(binary.BigEndian.Uint16(buf[6:8]))
|
// trainVobcInfo.BrakeForce = int64(binary.BigEndian.Uint16(buf[6:8]))
|
||||||
trainVobcInfo.TrainLoad = int64(binary.BigEndian.Uint16(buf[8:10]))
|
// trainVobcInfo.TrainLoad = int64(binary.BigEndian.Uint16(buf[8:10]))
|
||||||
b4 := buf[15]
|
// b4 := buf[15]
|
||||||
trainVobcInfo.LeftDoorOpenCommand = (b4 & 1) != 0
|
// trainVobcInfo.LeftDoorOpenCommand = (b4 & 1) != 0
|
||||||
trainVobcInfo.RightDoorOpenCommand = (b4 & (1 << 1)) != 0
|
// trainVobcInfo.RightDoorOpenCommand = (b4 & (1 << 1)) != 0
|
||||||
trainVobcInfo.LeftDoorCloseCommand = (b4 & (1 << 2)) != 0
|
// trainVobcInfo.LeftDoorCloseCommand = (b4 & (1 << 2)) != 0
|
||||||
trainVobcInfo.RightDoorCloseCommand = (b4 & (1 << 3)) != 0
|
// trainVobcInfo.RightDoorCloseCommand = (b4 & (1 << 3)) != 0
|
||||||
trainVobcInfo.AllDoorClose = (b4 & (1 << 4)) != 0
|
// trainVobcInfo.AllDoorClose = (b4 & (1 << 4)) != 0
|
||||||
return trainVobcInfo
|
// return trainVobcInfo
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 发送给前端的速度格式化
|
// // 发送给前端的速度格式化
|
||||||
func speedParse(speed float32) int32 {
|
// func speedParse(speed float32) int32 {
|
||||||
return int32(math.Abs(float64(speed * 3.6 * 100)))
|
// return int32(math.Abs(float64(speed * 3.6 * 100)))
|
||||||
}
|
// }
|
||||||
|
|
|
@ -4,13 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"joylink.club/bj-rtsts-server/dto"
|
"joylink.club/bj-rtsts-server/dto"
|
||||||
"joylink.club/bj-rtsts-server/dynamics"
|
"joylink.club/bj-rtsts-server/third_party/dynamics"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/message"
|
||||||
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
"joylink.club/bj-rtsts-server/ats/verify/protos/state"
|
"joylink.club/bj-rtsts-server/ats/verify/protos/state"
|
||||||
|
@ -38,7 +38,7 @@ func AddTrainState(vs *VerifySimulation, status *state.TrainState, mapId int32)
|
||||||
status.Up = up
|
status.Up = up
|
||||||
status.PointTo = pointTo
|
status.PointTo = pointTo
|
||||||
status.TrainKilometer = kilometer
|
status.TrainKilometer = kilometer
|
||||||
httpCode, _, err := dynamics.SendInitTrainReq(&dynamics.InitTrainInfo{
|
err := dynamics.Default().RequestAddTrain(&message.InitTrainInfo{
|
||||||
TrainIndex: uint16(trainIndex),
|
TrainIndex: uint16(trainIndex),
|
||||||
LinkIndex: uint16(linkId),
|
LinkIndex: uint16(linkId),
|
||||||
LinkOffset: uint32(loffset),
|
LinkOffset: uint32(loffset),
|
||||||
|
@ -46,10 +46,18 @@ func AddTrainState(vs *VerifySimulation, status *state.TrainState, mapId int32)
|
||||||
Up: status.Up,
|
Up: status.Up,
|
||||||
TrainLength: uint16(status.TrainLength),
|
TrainLength: uint16(status.TrainLength),
|
||||||
})
|
})
|
||||||
|
// httpCode, _, err := dynamics.SendInitTrainReq(&dynamics.InitTrainInfo{
|
||||||
|
// TrainIndex: uint16(trainIndex),
|
||||||
|
// LinkIndex: uint16(linkId),
|
||||||
|
// LinkOffset: uint32(loffset),
|
||||||
|
// Speed: uint16(math.Round(float64(status.Speed * 10))),
|
||||||
|
// Up: status.Up,
|
||||||
|
// TrainLength: uint16(status.TrainLength),
|
||||||
|
// })
|
||||||
slog.Debug("添加列车", "trainIndex", trainIndex, "HeadDeviceId", status.HeadDeviceId, "HeadOffset", status.HeadOffset)
|
slog.Debug("添加列车", "trainIndex", trainIndex, "HeadDeviceId", status.HeadDeviceId, "HeadOffset", status.HeadOffset)
|
||||||
slog.Debug("列车初始化", "trainIndex", trainIndex, "linkId", linkId, "loffset", loffset)
|
slog.Debug("列车初始化", "trainIndex", trainIndex, "linkId", linkId, "loffset", loffset)
|
||||||
if err != nil || httpCode != http.StatusOK {
|
if err != nil {
|
||||||
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)})
|
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()})
|
||||||
}
|
}
|
||||||
// 调用成功后初始化列车的动力学
|
// 调用成功后初始化列车的动力学
|
||||||
status.DynamicState = &state.TrainDynamicState{}
|
status.DynamicState = &state.TrainDynamicState{}
|
||||||
|
@ -84,11 +92,14 @@ func RemoveTrainState(vs *VerifySimulation, id string) {
|
||||||
if ok {
|
if ok {
|
||||||
t := d.(*state.TrainState)
|
t := d.(*state.TrainState)
|
||||||
trainIndex, _ := strconv.ParseUint(id, 10, 16)
|
trainIndex, _ := strconv.ParseUint(id, 10, 16)
|
||||||
httpCode, _, err := dynamics.SendRemoveTrainReq(&dynamics.InitTrainInfo{
|
err := dynamics.Default().RequestRemoveTrain(&message.RemoveTrainReq{
|
||||||
TrainIndex: uint16(trainIndex),
|
TrainIndex: uint16(trainIndex),
|
||||||
})
|
})
|
||||||
if err != nil || httpCode != http.StatusOK {
|
// httpCode, _, err := dynamics.SendRemoveTrainReq(&dynamics.InitTrainInfo{
|
||||||
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)})
|
// TrainIndex: uint16(trainIndex),
|
||||||
|
// })
|
||||||
|
if err != nil {
|
||||||
|
panic(dto.ErrorDto{Code: dto.DynamicsError, Message: err.Error()})
|
||||||
}
|
}
|
||||||
// 从仿真内存中移除列车
|
// 从仿真内存中移除列车
|
||||||
t.Show = false
|
t.Show = false
|
||||||
|
|
|
@ -2,6 +2,8 @@ package memory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -12,6 +14,8 @@ import (
|
||||||
"joylink.club/bj-rtsts-server/ats/verify/protos/graphicData"
|
"joylink.club/bj-rtsts-server/ats/verify/protos/graphicData"
|
||||||
"joylink.club/bj-rtsts-server/ats/verify/protos/state"
|
"joylink.club/bj-rtsts-server/ats/verify/protos/state"
|
||||||
"joylink.club/bj-rtsts-server/dto"
|
"joylink.club/bj-rtsts-server/dto"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/deprecated/vobc"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/message"
|
||||||
"joylink.club/ecs"
|
"joylink.club/ecs"
|
||||||
"joylink.club/rtsssimulation/repository"
|
"joylink.club/rtsssimulation/repository"
|
||||||
"joylink.club/rtsssimulation/repository/model/proto"
|
"joylink.club/rtsssimulation/repository/model/proto"
|
||||||
|
@ -163,6 +167,158 @@ func (s *VerifySimulation) GetComIdByUid(uid string) string {
|
||||||
return es[uid].CommonId
|
return es[uid].CommonId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sim *VerifySimulation) BuildLineBaseInfo() *message.LineBaseInfo {
|
||||||
|
info := &message.LineBaseInfo{}
|
||||||
|
for _, model := range sim.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(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 := &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 sim.Repo.SectionalCurvatureList() {
|
||||||
|
id, _ := strconv.Atoi(sim.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 (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
|
||||||
|
}
|
||||||
|
trainState := sta.(*state.TrainState)
|
||||||
|
// 给半实物仿真发送速度
|
||||||
|
vobc.SendTrainSpeedTask(math.Abs(float64(info.Speed * 36)))
|
||||||
|
// 更新列车状态
|
||||||
|
UpdateTrainState(s, convert(info, trainState, s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert(info *message.DynamicsTrainInfo, sta *state.TrainState, simulation *VerifySimulation) *state.TrainState {
|
||||||
|
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.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 speedParse(speed float32) int32 {
|
||||||
|
return int32(math.Abs(float64(speed * 3.6 * 100)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理半实物仿真列车控制消息
|
||||||
|
func (s *VerifySimulation) HandleSemiPhysicalTrainControlMsg(msg *message.TrainControlMsg) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func buildProtoRepository(mapIds []int32) (*proto.Repository, error) {
|
func buildProtoRepository(mapIds []int32) (*proto.Repository, error) {
|
||||||
repo := &proto.Repository{}
|
repo := &proto.Repository{}
|
||||||
var exceptStationGiMapIds []int32
|
var exceptStationGiMapIds []int32
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 3f409d717bab334f3d39a5af043c5f00881eaddd
|
Subproject commit 5ec37803d5699d93ae8d42a67e4f04a7b6b34afc
|
|
@ -11,7 +11,7 @@ dynamics:
|
||||||
udpRemotePort: 3000
|
udpRemotePort: 3000
|
||||||
udpRemoteTrainPort: 3001
|
udpRemoteTrainPort: 3001
|
||||||
httpPort: 7800
|
httpPort: 7800
|
||||||
open: false
|
open: true
|
||||||
# VOBC
|
# VOBC
|
||||||
vobc:
|
vobc:
|
||||||
ip: 10.60.1.59
|
ip: 10.60.1.59
|
||||||
|
|
8
main.go
8
main.go
|
@ -8,9 +8,9 @@ import (
|
||||||
"joylink.club/bj-rtsts-server/api"
|
"joylink.club/bj-rtsts-server/api"
|
||||||
"joylink.club/bj-rtsts-server/config"
|
"joylink.club/bj-rtsts-server/config"
|
||||||
"joylink.club/bj-rtsts-server/docs"
|
"joylink.club/bj-rtsts-server/docs"
|
||||||
"joylink.club/bj-rtsts-server/dynamics"
|
|
||||||
"joylink.club/bj-rtsts-server/middleware"
|
"joylink.club/bj-rtsts-server/middleware"
|
||||||
"joylink.club/bj-rtsts-server/vobc"
|
|
||||||
|
"joylink.club/bj-rtsts-server/third_party"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title CBTC测试系统API
|
// @title CBTC测试系统API
|
||||||
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
engine := InitServer()
|
engine := InitServer()
|
||||||
|
third_party.Init()
|
||||||
authMiddleware := middleware.InitGinJwtMiddleware()
|
authMiddleware := middleware.InitGinJwtMiddleware()
|
||||||
router := engine.Group("/api")
|
router := engine.Group("/api")
|
||||||
api.InitUserRouter(router, authMiddleware)
|
api.InitUserRouter(router, authMiddleware)
|
||||||
|
@ -40,9 +41,6 @@ func main() {
|
||||||
docs.SwaggerInfo.Title = "CBTC测试系统API"
|
docs.SwaggerInfo.Title = "CBTC测试系统API"
|
||||||
engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
|
||||||
go dynamics.RunUdpServer()
|
|
||||||
go vobc.RunUdpServer()
|
|
||||||
|
|
||||||
serverConfig := config.Config.Server
|
serverConfig := config.Config.Server
|
||||||
if serverConfig.Port == 0 {
|
if serverConfig.Port == 0 {
|
||||||
serverConfig.Port = 8080
|
serverConfig.Port = 8080
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 93725ce93431b07d1d56863eaa8a3f5f77d3a39f
|
Subproject commit 30c4210a5d0411bb52d383bef7e77de7b9484800
|
|
@ -10,6 +10,11 @@ type InitTrainInfo struct {
|
||||||
TrainLength uint16 `json:"trainLength"`
|
TrainLength uint16 `json:"trainLength"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 移除列车请求参数
|
||||||
|
type RemoveTrainReq struct {
|
||||||
|
TrainIndex uint16 `json:"trainIndex"`
|
||||||
|
}
|
||||||
|
|
||||||
// LineBaseInfo 线路基础信息,提供给动力学作为计算依据
|
// LineBaseInfo 线路基础信息,提供给动力学作为计算依据
|
||||||
type LineBaseInfo struct {
|
type LineBaseInfo struct {
|
||||||
LinkList []*Link `json:"linkList"`
|
LinkList []*Link `json:"linkList"`
|
|
@ -0,0 +1,239 @@
|
||||||
|
package dynamics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"joylink.club/bj-rtsts-server/config"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/message"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DynamicsMessageManager interface {
|
||||||
|
CollectDynamicsTurnoutInfo() []*message.DynamicsTurnoutInfo
|
||||||
|
HandleDynamicsTrainInfo(info *message.DynamicsTrainInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动力学接口
|
||||||
|
type Dynamics interface {
|
||||||
|
// 请求启动仿真
|
||||||
|
RequestStartSimulation(base *message.LineBaseInfo) error
|
||||||
|
// 请求停止仿真
|
||||||
|
RequestStopSimulation() error
|
||||||
|
// 请求添加列车
|
||||||
|
RequestAddTrain(info *message.InitTrainInfo) error
|
||||||
|
// 请求移除列车
|
||||||
|
RequestRemoveTrain(req *message.RemoveTrainReq) error
|
||||||
|
|
||||||
|
// 启动动力学消息功能
|
||||||
|
Start(manager DynamicsMessageManager)
|
||||||
|
// 停止动力学消息功能
|
||||||
|
Stop()
|
||||||
|
|
||||||
|
// 发送列车控制消息
|
||||||
|
SendTrainControlMessage(b []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _default Dynamics
|
||||||
|
|
||||||
|
func Default() Dynamics {
|
||||||
|
if !config.Config.Dynamics.Open {
|
||||||
|
panic("动力学接口模块未开启")
|
||||||
|
}
|
||||||
|
return _default
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
if !config.Config.Dynamics.Open {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slog.Info("初始化动力学接口模块")
|
||||||
|
_default = newDynamics()
|
||||||
|
}
|
||||||
|
|
||||||
|
type dynamics struct {
|
||||||
|
trainInfoUdpServer udp.UdpServer
|
||||||
|
turnoutStateUdpClient udp.UdpClient
|
||||||
|
trainControlUdpClient udp.UdpClient
|
||||||
|
|
||||||
|
baseUrl string
|
||||||
|
httpClient *http.Client
|
||||||
|
manager DynamicsMessageManager
|
||||||
|
turnoutTaskCancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDynamics() Dynamics {
|
||||||
|
d := &dynamics{
|
||||||
|
turnoutStateUdpClient: udp.NewClient(fmt.Sprintf("%v:%v", config.Config.Dynamics.Ip, config.Config.Dynamics.UdpRemotePort)),
|
||||||
|
trainControlUdpClient: udp.NewClient(fmt.Sprintf("%v:%v", config.Config.Dynamics.Ip, config.Config.Dynamics.UdpRemoteTrainPort)),
|
||||||
|
baseUrl: getUrlBase(),
|
||||||
|
httpClient: &http.Client{
|
||||||
|
Timeout: time.Second * 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
d.trainInfoUdpServer = udp.NewServer(fmt.Sprintf(":%d", config.Config.Dynamics.UdpLocalPort), d.handleDynamicsTrainInfo)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解码列车信息并处理
|
||||||
|
func (d *dynamics) handleDynamicsTrainInfo(b []byte) {
|
||||||
|
trainInfo := &message.DynamicsTrainInfo{}
|
||||||
|
err := trainInfo.Decode(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
handler := d.manager
|
||||||
|
if handler != nil {
|
||||||
|
handler.HandleDynamicsTrainInfo(trainInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUrlBase() string {
|
||||||
|
ip := config.Config.Dynamics.Ip
|
||||||
|
var port string
|
||||||
|
if config.Config.Dynamics.HttpPort != 0 {
|
||||||
|
port = fmt.Sprintf(":%d", config.Config.Dynamics.HttpPort)
|
||||||
|
}
|
||||||
|
urlBase := "http://" + ip + port
|
||||||
|
return urlBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) buildUrl(uri string) string {
|
||||||
|
return d.baseUrl + uri
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) RequestStartSimulation(base *message.LineBaseInfo) error {
|
||||||
|
if !config.Config.Dynamics.Open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
url := d.buildUrl("/api/start/")
|
||||||
|
data, _ := json.Marshal(base)
|
||||||
|
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("请求启动仿真异常: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
_, err = resp.Body.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("请求启动仿真读取相应异常: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) RequestStopSimulation() error {
|
||||||
|
if !config.Config.Dynamics.Open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
url := d.buildUrl("/api/end/")
|
||||||
|
resp, err := d.httpClient.Post(url, "application/json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("停止仿真请求异常: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
var buf []byte
|
||||||
|
_, err = resp.Body.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("停止仿真响应读取异常: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) RequestAddTrain(info *message.InitTrainInfo) error {
|
||||||
|
if !config.Config.Dynamics.Open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
url := d.buildUrl("/api/aerodynamics/init/train/")
|
||||||
|
data, _ := json.Marshal(info)
|
||||||
|
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("动力学添加列车请求异常: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
var buf []byte
|
||||||
|
_, err = resp.Body.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("动力学添加列车响应读取异常: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) RequestRemoveTrain(req *message.RemoveTrainReq) error {
|
||||||
|
if !config.Config.Dynamics.Open {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
url := d.buildUrl("/api/aerodynamics/remove/train/")
|
||||||
|
data, _ := json.Marshal(req)
|
||||||
|
resp, err := d.httpClient.Post(url, "application/json", bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("动力学移除列车请求异常: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
var buf []byte
|
||||||
|
_, err = resp.Body.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("动力学移除列车响应读取异常: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) Start(manager DynamicsMessageManager) {
|
||||||
|
if manager == nil {
|
||||||
|
panic("启动动力学消息服务错误: DynamicsMessageManager不能为nil")
|
||||||
|
}
|
||||||
|
if d.manager != nil {
|
||||||
|
panic("启动动力学消息服务错误: 存在正在运行的任务")
|
||||||
|
}
|
||||||
|
d.manager = manager
|
||||||
|
ctx, cancle := context.WithCancel(context.Background())
|
||||||
|
go d.sendTurnoutStateTask(ctx)
|
||||||
|
d.turnoutTaskCancel = cancle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) Stop() {
|
||||||
|
if d.turnoutTaskCancel != nil {
|
||||||
|
d.turnoutTaskCancel()
|
||||||
|
d.manager = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 道岔消息发送间隔,单位ms
|
||||||
|
TurnoutMessageSendInterval = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
var turnoutStateLifeSignal uint16 //道岔消息生命信号
|
||||||
|
|
||||||
|
// 定时发送道岔状态任务
|
||||||
|
func (d *dynamics) sendTurnoutStateTask(ctx context.Context) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
slog.Error("定时发送道岔状态任务异常", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
turnoutStates := d.manager.CollectDynamicsTurnoutInfo()
|
||||||
|
slog.Debug("发送道岔状态", "count", len(turnoutStates))
|
||||||
|
for _, state := range turnoutStates {
|
||||||
|
turnoutStateLifeSignal++
|
||||||
|
state.LifeSignal = turnoutStateLifeSignal
|
||||||
|
d.turnoutStateUdpClient.SendMsg(state)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * TurnoutMessageSendInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dynamics) SendTrainControlMessage(b []byte) {
|
||||||
|
d.trainControlUdpClient.Send(b)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestMsg struct {
|
||||||
|
Msg string
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestMsg) Encode() []byte {
|
||||||
|
b := []byte(t.Msg)
|
||||||
|
// binary.BigEndian.PutUint16(b, uint16(t.Port))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestMsg) Decode(data []byte) error {
|
||||||
|
t.Msg = string(data)
|
||||||
|
// t.Port = int(binary.BigEndian.Uint16(data))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUdpMsg(b []byte) {
|
||||||
|
slog.Info("udp server handle", "msg", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
udp.NewServer("127.0.0.1:6666", handleUdpMsg).Listen()
|
||||||
|
|
||||||
|
// client := udp.NewClient("127.0.0.1:7777")
|
||||||
|
// for i := 0; i < 1000; i++ {
|
||||||
|
// time.Sleep(time.Millisecond * 500)
|
||||||
|
// client.Send(&TestMsg{
|
||||||
|
// Msg: "test, port = 7777",
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 60)
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DynamicsTurnoutInfo struct {
|
||||||
|
LifeSignal uint16
|
||||||
|
Code uint16
|
||||||
|
NPosition bool
|
||||||
|
RPosition bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DynamicsTurnoutInfo) Encode() []byte {
|
||||||
|
var data []byte
|
||||||
|
data = binary.BigEndian.AppendUint16(data, t.LifeSignal)
|
||||||
|
data = binary.BigEndian.AppendUint16(data, t.Code)
|
||||||
|
var b byte
|
||||||
|
if t.NPosition {
|
||||||
|
b |= 1 << 7
|
||||||
|
}
|
||||||
|
if t.RPosition {
|
||||||
|
b |= 1 << 6
|
||||||
|
}
|
||||||
|
data = append(data, b)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicsTrainInfo struct {
|
||||||
|
//生命信号
|
||||||
|
LifeSignal uint16
|
||||||
|
//列车号(车辆)
|
||||||
|
Number uint8
|
||||||
|
//列车长度 cm
|
||||||
|
Len uint16
|
||||||
|
//列车所在轨道link
|
||||||
|
Link uint8
|
||||||
|
//列车所在link偏移量(mm)
|
||||||
|
LinkOffset uint32
|
||||||
|
//列车所在位置坡度值(‰)
|
||||||
|
Slope uint16
|
||||||
|
//列车所在位置坡度走势(上/下坡)
|
||||||
|
UpSlope bool
|
||||||
|
//列车当前运行方向(偏移量增大/减小方向)
|
||||||
|
Up bool
|
||||||
|
//实际运行阻力(总)(N)
|
||||||
|
TotalResistance int32
|
||||||
|
//阻力1(空气阻力)(N)
|
||||||
|
AirResistance int32
|
||||||
|
//阻力2(坡道阻力)(N)
|
||||||
|
SlopeResistance int32
|
||||||
|
//阻力3(曲线阻力)(N)
|
||||||
|
CurveResistance int32
|
||||||
|
//列车运行速度(m/s)
|
||||||
|
Speed float32
|
||||||
|
//头车速传1速度值(m/s)
|
||||||
|
HeadSpeed1 float32
|
||||||
|
//头车速度2速度值(m/s)
|
||||||
|
HeadSpeed2 float32
|
||||||
|
//尾车速传1速度值(m/s)
|
||||||
|
TailSpeed1 float32
|
||||||
|
//尾车速度2速度值(m/s)
|
||||||
|
TailSpeed2 float32
|
||||||
|
//头车雷达速度值(m/s)
|
||||||
|
HeadRadarSpeed float32
|
||||||
|
//尾车雷达速度值(m/s)
|
||||||
|
TailRadarSpeed float32
|
||||||
|
//加速度(m/s^2)
|
||||||
|
Acceleration float32
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析动力学的列车信息
|
||||||
|
func (t *DynamicsTrainInfo) Decode(buf []byte) error {
|
||||||
|
t.LifeSignal = binary.BigEndian.Uint16(buf[0:2])
|
||||||
|
t.Number = buf[2]
|
||||||
|
t.Len = binary.BigEndian.Uint16(buf[3:5])
|
||||||
|
t.Link = buf[5]
|
||||||
|
t.LinkOffset = binary.BigEndian.Uint32(buf[6:10])
|
||||||
|
t.Slope = binary.BigEndian.Uint16(buf[10:12])
|
||||||
|
b := buf[12]
|
||||||
|
t.UpSlope = (b & (1 << 7)) != 0
|
||||||
|
t.Up = (b & (1 << 6)) != 0
|
||||||
|
t.TotalResistance = int32(binary.BigEndian.Uint32(buf[14:18]))
|
||||||
|
t.AirResistance = int32(binary.BigEndian.Uint32(buf[18:22]))
|
||||||
|
t.SlopeResistance = int32(binary.BigEndian.Uint32(buf[22:26]))
|
||||||
|
t.CurveResistance = int32(binary.BigEndian.Uint32(buf[26:30]))
|
||||||
|
t.Speed = math.Float32frombits(binary.BigEndian.Uint32(buf[30:34]))
|
||||||
|
t.HeadSpeed1 = math.Float32frombits(binary.BigEndian.Uint32(buf[34:38]))
|
||||||
|
t.HeadSpeed2 = math.Float32frombits(binary.BigEndian.Uint32(buf[38:42]))
|
||||||
|
t.TailSpeed1 = math.Float32frombits(binary.BigEndian.Uint32(buf[42:46]))
|
||||||
|
t.TailSpeed2 = math.Float32frombits(binary.BigEndian.Uint32(buf[46:50]))
|
||||||
|
t.HeadRadarSpeed = math.Float32frombits(binary.BigEndian.Uint32(buf[50:54]))
|
||||||
|
t.TailRadarSpeed = math.Float32frombits(binary.BigEndian.Uint32(buf[54:58]))
|
||||||
|
t.Acceleration = math.Float32frombits(binary.BigEndian.Uint32(buf[58:62]))
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
type InitTrainInfo struct {
|
||||||
|
TrainIndex uint16 `json:"trainIndex"`
|
||||||
|
LinkIndex uint16 `json:"linkIndex"`
|
||||||
|
LinkOffset uint32 `json:"linkOffset"`
|
||||||
|
//单位0.1km/h
|
||||||
|
Speed uint16 `json:"speed"`
|
||||||
|
Up bool `json:"up"`
|
||||||
|
TrainLength uint16 `json:"trainLength"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除列车请求参数
|
||||||
|
type RemoveTrainReq struct {
|
||||||
|
TrainIndex uint16 `json:"trainIndex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineBaseInfo 线路基础信息,提供给动力学作为计算依据
|
||||||
|
type LineBaseInfo struct {
|
||||||
|
LinkList []*Link `json:"linkList"`
|
||||||
|
SlopeList []*Slope `json:"slopeList"`
|
||||||
|
CurveList []*Curve `json:"curveList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
//长度 mm
|
||||||
|
Len int32 `json:"len"`
|
||||||
|
ARelTurnoutId int32 `json:"ARelTurnoutId"`
|
||||||
|
ARelTurnoutPoint string `json:"ARelTurnoutPoint"`
|
||||||
|
BRelTurnoutId int32 `json:"BRelTurnoutId"`
|
||||||
|
BRelTurnoutPoint string `json:"BRelTurnoutPoint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Slope struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
StartLinkId int32 `json:"startLinkId"`
|
||||||
|
StartLinkOffset int32 `json:"startLinkOffset"`
|
||||||
|
EndLinkId int32 `json:"endLinkId"`
|
||||||
|
EndLinkOffset int32 `json:"endLinkOffset"`
|
||||||
|
//坡度的三角函数(猜是sin)值的*1000
|
||||||
|
DegreeTrig int32 `json:"degreeTrig"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Curve struct {
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
StartLinkId int32 `json:"startLinkId"`
|
||||||
|
StartLinkOffset int32 `json:"startLinkOffset"`
|
||||||
|
EndLinkId int32 `json:"endLinkId"`
|
||||||
|
EndLinkOffset int32 `json:"endLinkOffset"`
|
||||||
|
Curvature int32 `json:"curvature"`
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package message
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
// 接收到的列车控制信息
|
||||||
|
type TrainControlMsg struct {
|
||||||
|
//【0 1】两个字节
|
||||||
|
// 生命信号 每个周期+1
|
||||||
|
LifeSignal uint16
|
||||||
|
//【2】 一个字节
|
||||||
|
// TC1激活状态 1=激活
|
||||||
|
Tc1Active bool
|
||||||
|
// TC2激活状态 1=激活
|
||||||
|
Tc2Active bool
|
||||||
|
// 列车方向向前 1=方向向前
|
||||||
|
DirectionForward bool
|
||||||
|
// 列车方向向后 1=方向向后
|
||||||
|
DirectionBackward bool
|
||||||
|
// 列车牵引状态 1=牵引
|
||||||
|
TractionStatus bool
|
||||||
|
// 列车制动状态 1=制动
|
||||||
|
BrakingStatus bool
|
||||||
|
// 列车紧急制动状态 1=紧急制动
|
||||||
|
EmergencyBrakingStatus bool
|
||||||
|
// 列车折返状态(AR) 1=折返
|
||||||
|
TurnbackStatus bool
|
||||||
|
//【3】 一个字节
|
||||||
|
// 跳跃状态 1=跳跃
|
||||||
|
JumpStatus bool
|
||||||
|
// ATO模式 1=ATO模式
|
||||||
|
ATO bool
|
||||||
|
// FAM模式 1=FAM模式
|
||||||
|
FAM bool
|
||||||
|
// CAM模式 1=CAM模式
|
||||||
|
CAM bool
|
||||||
|
// 牵引安全回路 1=牵引安全切除
|
||||||
|
TractionSafetyCircuit bool
|
||||||
|
// 停放制动状态 1=停放施加
|
||||||
|
ParkingBrakeStatus bool
|
||||||
|
// 保持制动状态 1=保持制动施加
|
||||||
|
MaintainBrakeStatus bool
|
||||||
|
//【4 5】 两个字节 列车牵引力 100=1KN
|
||||||
|
TractionForce uint16
|
||||||
|
//【6 7】 列车制动力 100=1KN
|
||||||
|
BrakeForce uint16
|
||||||
|
//【8 9】 列车载荷 100=1ton
|
||||||
|
TrainLoad uint16
|
||||||
|
// 【15】 一个字节
|
||||||
|
// 列车开左门指令 1=开门
|
||||||
|
LeftDoorOpenCommand bool
|
||||||
|
// 列车开右门指令 1=开门
|
||||||
|
RightDoorOpenCommand bool
|
||||||
|
// 列车关左门指令 1=关门
|
||||||
|
LeftDoorCloseCommand bool
|
||||||
|
// 列车关右门指令 1=关门
|
||||||
|
RightDoorCloseCommand bool
|
||||||
|
// 整列车门关好 1=门关好
|
||||||
|
AllDoorClose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析VOBC列车信息
|
||||||
|
func (r *TrainControlMsg) DecoderVobcTrainInfo(buf []byte) *TrainControlMsg {
|
||||||
|
r.LifeSignal = binary.BigEndian.Uint16(buf[0:2])
|
||||||
|
b2 := buf[2]
|
||||||
|
r.Tc1Active = (b2 & (1 << 7)) != 0
|
||||||
|
r.Tc2Active = (b2 & (1 << 6)) != 0
|
||||||
|
r.DirectionForward = (b2 & (1 << 5)) != 0
|
||||||
|
r.DirectionBackward = (b2 & (1 << 4)) != 0
|
||||||
|
r.TractionStatus = (b2 & (1 << 3)) != 0
|
||||||
|
r.BrakingStatus = (b2 & (1 << 2)) != 0
|
||||||
|
r.EmergencyBrakingStatus = (b2 & (1 << 1)) != 0
|
||||||
|
r.TurnbackStatus = (b2 & 1) != 0
|
||||||
|
b3 := buf[3]
|
||||||
|
r.JumpStatus = (b3 & (1 << 7)) != 0
|
||||||
|
r.ATO = (b3 & (1 << 6)) != 0
|
||||||
|
r.FAM = (b3 & (1 << 5)) != 0
|
||||||
|
r.CAM = (b3 & (1 << 4)) != 0
|
||||||
|
r.TractionSafetyCircuit = (b3 & (1 << 3)) != 0
|
||||||
|
r.ParkingBrakeStatus = (b3 & (1 << 2)) != 0
|
||||||
|
r.MaintainBrakeStatus = (b3 & (1 << 1)) != 0
|
||||||
|
r.TractionForce = binary.BigEndian.Uint16(buf[4:6])
|
||||||
|
r.BrakeForce = binary.BigEndian.Uint16(buf[6:8])
|
||||||
|
r.TrainLoad = binary.BigEndian.Uint16(buf[8:10])
|
||||||
|
b4 := buf[15]
|
||||||
|
r.LeftDoorOpenCommand = (b4 & (1 << 7)) != 0
|
||||||
|
r.RightDoorOpenCommand = (b4 & (1 << 6)) != 0
|
||||||
|
r.LeftDoorCloseCommand = (b4 & (1 << 5)) != 0
|
||||||
|
r.RightDoorCloseCommand = (b4 & (1 << 4)) != 0
|
||||||
|
r.AllDoorClose = (b4 & (1 << 3)) != 0
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送列车信息
|
||||||
|
type TrainSpeedMsg struct {
|
||||||
|
// 生命信号 每个周期+1
|
||||||
|
LifeSignal uint16
|
||||||
|
// 列车速度 10=1km/h
|
||||||
|
Speed uint16
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package semi_physical_train
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"joylink.club/bj-rtsts-server/config"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/message"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 半实物仿真列车通信接口
|
||||||
|
type SemiPhysicalTrain interface {
|
||||||
|
// 启动半实物仿真消息处理
|
||||||
|
Start(manager SemiPhysicalMessageManager)
|
||||||
|
// 停止半实物仿真消息处理
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type SemiPhysicalMessageManager interface {
|
||||||
|
// 处理半实物仿真列车控制消息
|
||||||
|
HandleSemiPhysicalTrainControlMsg(msg *message.TrainControlMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type semiPhysicalTrainImpl struct {
|
||||||
|
trainControlUdpServer udp.UdpServer
|
||||||
|
trainSpeedInfoUdpClient udp.UdpClient
|
||||||
|
|
||||||
|
manager SemiPhysicalMessageManager
|
||||||
|
}
|
||||||
|
|
||||||
|
var _default SemiPhysicalTrain
|
||||||
|
|
||||||
|
func Default() SemiPhysicalTrain {
|
||||||
|
if !config.Config.Vobc.Open {
|
||||||
|
panic("半实物仿真接口模块未开启")
|
||||||
|
}
|
||||||
|
return _default
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
if !config.Config.Vobc.Open {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_default = newSemiPhysicalTrain()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSemiPhysicalTrain() SemiPhysicalTrain {
|
||||||
|
s := &semiPhysicalTrainImpl{
|
||||||
|
trainSpeedInfoUdpClient: udp.NewClient("127.0.0.1:7777"),
|
||||||
|
}
|
||||||
|
s.trainControlUdpServer = udp.NewServer(fmt.Sprintf(":%d", config.Config.Dynamics.UdpLocalPort), s.handleTrainControlMsg)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *semiPhysicalTrainImpl) handleTrainControlMsg(b []byte) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *semiPhysicalTrainImpl) Start(manager SemiPhysicalMessageManager) {
|
||||||
|
s.manager = manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *semiPhysicalTrainImpl) Stop() {
|
||||||
|
s.manager = nil
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package third_party
|
||||||
|
|
||||||
|
import (
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/dynamics"
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/semi_physical_train"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
dynamics.Init()
|
||||||
|
semi_physical_train.Init()
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UdpClient interface {
|
||||||
|
SendMsg(msg UdpMessageEncoder)
|
||||||
|
Send(b []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
type client struct {
|
||||||
|
laddr *net.UDPAddr
|
||||||
|
raddr *net.UDPAddr
|
||||||
|
conn *net.UDPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建UDP客户端
|
||||||
|
// remoteAddr - 远端地址, 如 127.0.0.1:7777
|
||||||
|
func NewClient(remoteAddr string) UdpClient {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var laddr *net.UDPAddr = nil
|
||||||
|
conn, err := net.DialUDP("udp", laddr, addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
c := &client{
|
||||||
|
laddr: laddr,
|
||||||
|
raddr: addr,
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新建UDP客户端
|
||||||
|
// remoteAddr - 远端地址, 如 127.0.0.1:7777
|
||||||
|
// localAddr - 本地地址, 如 :9999
|
||||||
|
func NewClientWithLocalAddr(remoteAddr string, localAddr string) UdpClient {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", remoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
laddr, err := net.ResolveUDPAddr("udp", localAddr)
|
||||||
|
if err != nil {
|
||||||
|
slog.Info("UDP客户端使用随机端口")
|
||||||
|
laddr = nil
|
||||||
|
}
|
||||||
|
conn, err := net.DialUDP("udp", laddr, addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
c := &client{
|
||||||
|
laddr: laddr,
|
||||||
|
raddr: addr,
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) SendMsg(msg UdpMessageEncoder) {
|
||||||
|
b := msg.Encode()
|
||||||
|
_, err := c.conn.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("udp client send error", "error", err)
|
||||||
|
}
|
||||||
|
// slog.Debug("udp client send", "size", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) Send(b []byte) {
|
||||||
|
_, err := c.conn.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("udp client send error", "error", err)
|
||||||
|
}
|
||||||
|
// slog.Debug("udp client send", "size", n)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package udp
|
||||||
|
|
||||||
|
type UdpMessageCodec interface {
|
||||||
|
UdpMessageEncoder
|
||||||
|
UdpMessageDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
type UdpMessageEncoder interface {
|
||||||
|
Encode() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type UdpMessageDecoder interface {
|
||||||
|
Decode(data []byte) error
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UdpServer interface {
|
||||||
|
Listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
type UdpMsgHandler func(b []byte)
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
addr string
|
||||||
|
conn *net.UDPConn
|
||||||
|
handler UdpMsgHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer creates a new instance of UdpServer.
|
||||||
|
func NewServer(addr string, handler UdpMsgHandler) UdpServer {
|
||||||
|
return &server{addr: addr, handler: handler}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) Listen() {
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", s.addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
conn, err := net.ListenUDP("udp", udpAddr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.conn = conn
|
||||||
|
// 启动监听处理
|
||||||
|
go s.listenAndHandle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) listenAndHandle() {
|
||||||
|
defer s.conn.Close()
|
||||||
|
for {
|
||||||
|
b := make([]byte, 1024)
|
||||||
|
n, err := s.conn.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("udp server read error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
go s.handle(b[0:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) handle(b []byte) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
slog.Error("udp server handle error", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// slog.Info("udp server handle", "msg", string(b))
|
||||||
|
s.handler(b)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package udp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"joylink.club/bj-rtsts-server/third_party/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestMsg struct {
|
||||||
|
Msg string
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestMsg) Encode() []byte {
|
||||||
|
b := []byte(t.Msg)
|
||||||
|
// binary.BigEndian.PutUint16(b, uint16(t.Port))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestMsg) Decode(data []byte) error {
|
||||||
|
t.Msg = string(data)
|
||||||
|
// t.Port = int(binary.BigEndian.Uint16(data))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUdpMsg(b []byte) {
|
||||||
|
slog.Info("udp server handle", "msg", string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUdpServer(t *testing.T) {
|
||||||
|
udp.NewServer("127.0.0.1:7777", handleUdpMsg).Listen()
|
||||||
|
|
||||||
|
client := udp.NewClient("127.0.0.1:7777")
|
||||||
|
client.SendMsg(&TestMsg{
|
||||||
|
Msg: "test, port = 7777",
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue