298 lines
9.6 KiB
Go
298 lines
9.6 KiB
Go
package simulation
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"net"
|
|
"strings"
|
|
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"go.uber.org/zap"
|
|
"joylink.club/bj-rtsts-server/ats/verify/protos/state"
|
|
"joylink.club/bj-rtsts-server/config"
|
|
"joylink.club/bj-rtsts-server/dynamics"
|
|
"joylink.club/bj-rtsts-server/vobc"
|
|
|
|
"joylink.club/bj-rtsts-server/ats/verify/simulation/wayside/memory"
|
|
"joylink.club/bj-rtsts-server/dto"
|
|
)
|
|
|
|
var simulationId_prefix = (func() string {
|
|
ip := "127.0.0.1"
|
|
addrList, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
for _, address := range addrList {
|
|
if ipNet, ok := address.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
|
if ipNet.IP.To4() != nil {
|
|
ip = ipNet.IP.String()
|
|
}
|
|
}
|
|
}
|
|
ipArr := strings.Split(ip, ".")
|
|
return ipArr[2] + "_" + ipArr[3]
|
|
})()
|
|
|
|
func init() {
|
|
// vobc 发来的列车信息
|
|
vobc.RegisterTrainInfoHandler(func(info []byte) {
|
|
zap.S().Debug("接到列车信息", info)
|
|
for _, simulation := range GetSimulationArr() {
|
|
simulation.Memory.Status.TrainStateMap.Range(func(key, value any) bool {
|
|
train := value.(*state.TrainState)
|
|
if train.Show { // 上线列车
|
|
// 拼接列车ID
|
|
trainId, err := strconv.Atoi(train.Id)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
trainInfo := decoderVobcTrainState(info)
|
|
zap.S().Info("接收到vobc发送的列车消息", trainInfo)
|
|
// 发送给动力学
|
|
dynamics.SendDynamicsTrainMsg(append(info, uint8(trainId)))
|
|
// 存放至仿真中
|
|
simulation.Memory.Status.VobcTrainStateMap.Store(train.Id, trainInfo)
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
})
|
|
dynamics.RegisterTrainInfoHandler(func(info *dynamics.TrainInfo) {
|
|
for _, simulation := range GetSimulationArr() {
|
|
sta, ok := simulation.Memory.Status.TrainStateMap.Load(strconv.Itoa(int(info.Number)))
|
|
if ok {
|
|
trainState := sta.(*state.TrainState)
|
|
// 给半实物仿真发送速度
|
|
zap.S().Info("发送到vobc发送的速度", info.Speed*36)
|
|
vobc.SendTrainSpeedTask(info.Speed * 36)
|
|
// 更新列车状态
|
|
memory.UpdateTrainState(simulation, convert(info, trainState, simulation))
|
|
break
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 仿真存储集合
|
|
var simulationMap sync.Map
|
|
|
|
// 创建仿真对象
|
|
func CreateSimulation(mapId int32) string {
|
|
simulationId := createSimulationId(mapId)
|
|
_, e := simulationMap.Load(simulationId)
|
|
if !e {
|
|
verifySimulation := memory.CreateSimulation(mapId, simulationId)
|
|
//通知动力学
|
|
httpCode, _, err := dynamics.SendSimulationStartReq(buildLineBaseInfo(memory.QueryMapVerifyStructure(verifySimulation.MapId)))
|
|
if httpCode != http.StatusOK || err != nil {
|
|
panic(dto.ErrorDto{Code: dto.LogicError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)})
|
|
}
|
|
simulationMap.Store(simulationId, verifySimulation)
|
|
dynamicsRun(verifySimulation)
|
|
}
|
|
return simulationId
|
|
}
|
|
|
|
// 删除仿真对象
|
|
func DestroySimulation(simulationId string) {
|
|
//移除道岔状态发送
|
|
dynamics.Stop()
|
|
//通知动力学
|
|
httpCode, _, err := dynamics.SendSimulationEndReq()
|
|
if httpCode != http.StatusOK || err != nil {
|
|
panic(dto.ErrorDto{Code: dto.LogicError, Message: fmt.Sprintf("动力学接口调用失败:[%d][%s]", httpCode, err)})
|
|
}
|
|
simulationMap.Delete(simulationId)
|
|
}
|
|
|
|
// 创建时生成仿真Id
|
|
func createSimulationId(mapId int32) string {
|
|
// 当前服务器IP + 端口 + 地图
|
|
return simulationId_prefix + "_" + strconv.Itoa(config.Config.Server.Port) + "_" + strconv.Itoa(int(mapId))
|
|
}
|
|
|
|
// 获取仿真列表
|
|
func ListAllSimulations() []*dto.SimulationInfoRepDto {
|
|
simArr := []*dto.SimulationInfoRepDto{}
|
|
simulationMap.Range(func(k, v any) bool {
|
|
s := v.(*memory.VerifySimulation)
|
|
simArr = append(simArr, &dto.SimulationInfoRepDto{
|
|
SimulationId: s.SimulationId,
|
|
MapId: strconv.Itoa(int(s.MapId)),
|
|
})
|
|
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 {
|
|
result := []*memory.VerifySimulation{}
|
|
simulationMap.Range(func(k, 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 {
|
|
vs := memory.QueryMapVerifyStructure(simulation.MapId)
|
|
|
|
zap.S().Debugf("原始消息:[%d-%d-%d]", info.Number, info.Link, info.LinkOffset)
|
|
sta.HeadLinkId = strconv.Itoa(int(info.Link))
|
|
sta.HeadLinkOffset = int64(info.LinkOffset)
|
|
//sta.Up = info.Up
|
|
id, port, offset, runDirection, pointTo := memory.QueryDeviceByCalcLink(vs, int32(info.Link), sta.HeadLinkOffset, sta.Up, sta.RunDirection)
|
|
zap.S().Debugf("转换后的消息:[%d-车头:%s-偏移:%d-上行:%v-是否ab:%v]", info.Number, id, offset, runDirection, pointTo)
|
|
sta.HeadDeviceId = id
|
|
sta.DevicePort = port
|
|
sta.HeadOffset = offset
|
|
sta.PointTo = pointTo
|
|
sta.RunDirection = runDirection
|
|
sta.Slope = int32(info.Slope)
|
|
sta.Upslope = info.UpSlope
|
|
sta.RunningUp = info.Up
|
|
sta.RunningResistanceSum = float32(info.TotalResistance) / 1000
|
|
sta.AirResistance = float32(info.AirResistance) / 1000
|
|
sta.RampResistance = float32(info.SlopeResistance) / 1000
|
|
sta.CurveResistance = float32(info.CurveResistance) / 1000
|
|
sta.Speed = info.Speed * 3.6
|
|
sta.HeadSensorSpeed1 = info.HeadSpeed1 * 3.6
|
|
sta.HeadSensorSpeed2 = info.HeadSpeed2 * 3.6
|
|
sta.TailSensorSpeed1 = info.TailSpeed1 * 3.6
|
|
sta.TailSensorSpeed2 = info.TailSpeed2 * 3.6
|
|
sta.HeadRadarSpeed = info.HeadRadarSpeed * 3.6
|
|
sta.TailRadarSpeed = info.TailRadarSpeed * 3.6
|
|
//判定车头方向
|
|
v, ok := simulation.Memory.Status.VobcTrainStateMap.Load(sta.Id)
|
|
if ok {
|
|
vobcState := v.(*state.TrainVobcState)
|
|
if vobcState.DirectionForward {
|
|
sta.HeadDirection = info.Up
|
|
} else if vobcState.DirectionBackward {
|
|
sta.HeadDirection = !info.Up
|
|
}
|
|
}
|
|
|
|
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 {
|
|
zap.S().Error("id转uint16报错", err)
|
|
}
|
|
info := dynamics.TurnoutInfo{
|
|
Code: uint16(code64),
|
|
NPosition: sta.Normal,
|
|
RPosition: sta.Reverse,
|
|
}
|
|
turnoutInfoSlice = append(turnoutInfoSlice, &info)
|
|
}
|
|
return turnoutInfoSlice
|
|
})
|
|
}
|
|
|
|
func buildLineBaseInfo(vs *memory.VerifyStructure) *dynamics.LineBaseInfo {
|
|
var links []*dynamics.Link
|
|
var slopes []*dynamics.Slope
|
|
var curves []*dynamics.Curve
|
|
for _, link := range vs.LinkModelMap {
|
|
id, _ := strconv.Atoi(link.Index)
|
|
var aTurnoutId int
|
|
if link.ARelatedSwitchRef.SwitchDevice != nil {
|
|
aTurnoutId, _ = strconv.Atoi(link.ARelatedSwitchRef.SwitchDevice.GetIndex())
|
|
}
|
|
var bTurnoutId int
|
|
if link.BRelatedSwitchRef.SwitchDevice != nil {
|
|
bTurnoutId, _ = strconv.Atoi(link.BRelatedSwitchRef.SwitchDevice.GetIndex())
|
|
}
|
|
links = append(links, &dynamics.Link{
|
|
ID: int32(id),
|
|
Len: link.Length,
|
|
ARelTurnoutId: int32(aTurnoutId),
|
|
ARelTurnoutPoint: link.ARelatedSwitchRef.Port.Name(),
|
|
BRelTurnoutId: int32(bTurnoutId),
|
|
BRelTurnoutPoint: link.BRelatedSwitchRef.Port.Name(),
|
|
})
|
|
}
|
|
//for _, slope := range vs.SlopeModelMap {
|
|
// id, _ := strconv.Atoi(slope.Index)
|
|
// slopes = append(slopes, &dynamics.Slope{
|
|
// ID: int32(id),
|
|
// StartLinkId: slope.StartLinkIndex,
|
|
// StartLinkOffset: slope.StartLinkOffset,
|
|
// EndLinkId: slope.EndLinkIndex,
|
|
// EndLinkOffset: slope.EndLinkOffset,
|
|
// DegreeTrig: slope.DegreeTrig,
|
|
// })
|
|
//}
|
|
//for _, curve := range vs.CurveModelMap {
|
|
// id, _ := strconv.Atoi(curve.Index)
|
|
// curves = append(curves, &dynamics.Curve{
|
|
// ID: int32(id),
|
|
// StartLinkId: curve.StartLinkIndex,
|
|
// StartLinkOffset: curve.StartLinkOffset,
|
|
// EndLinkId: curve.EndLinkIndex,
|
|
// EndLinkOffset: curve.EndLinkOffset,
|
|
// Curvature: curve.Curvature,
|
|
// })
|
|
//}
|
|
return &dynamics.LineBaseInfo{
|
|
LinkList: links,
|
|
SlopeList: slopes,
|
|
CurveList: curves,
|
|
}
|
|
}
|
|
|
|
// 解析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
|
|
}
|