列车pc仿真测试调整
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m35s
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m35s
This commit is contained in:
parent
a24ae0a14e
commit
7de95e55f2
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
Subproject commit e4728f0f732904f543a86aabb2c3da2653dd139f
|
||||
Subproject commit dd445689f17b65d274b39086ad525ae68d0e75c2
|
1
third_party/acc/acc_vobc.go
vendored
1
third_party/acc/acc_vobc.go
vendored
@ -67,6 +67,7 @@ func (acc *accVobcService) sendTask(ctx context.Context) {
|
||||
}
|
||||
trainStatus := acc.radarVobcManager.FindAccTrain()
|
||||
if trainStatus != nil {
|
||||
// 发送加速度信息
|
||||
speedAcc := trainStatus.DynamicState.Acceleration
|
||||
t := speedAcc / accSpeedUnit
|
||||
acc.vobcClient.SendMsg(&message.Accelerometer{Acc: math.Float32bits(t)})
|
||||
|
6
third_party/can_btm/balise_btm.go
vendored
6
third_party/can_btm/balise_btm.go
vendored
@ -65,12 +65,16 @@ func (s *btmCanetClient) GetState() state_proto.BTMState {
|
||||
} else {
|
||||
telegram = strings.Repeat("00", balise_const.UserTelegramByteLen)
|
||||
}
|
||||
var dis int64
|
||||
if info != nil {
|
||||
dis = info.Distance
|
||||
}
|
||||
return state_proto.BTMState{
|
||||
DataSerialNumber: uint32(s.dsn),
|
||||
BaliseCount: uint32(detector.baliseCounter),
|
||||
MessageCounter: uint32(detector.messageCounter),
|
||||
Telegram: telegram,
|
||||
Distance: info.Distance,
|
||||
Distance: dis,
|
||||
AboveBalise: detector.aboveBalise,
|
||||
}
|
||||
}
|
||||
|
10
third_party/dynamics/dynamics.go
vendored
10
third_party/dynamics/dynamics.go
vendored
@ -41,6 +41,8 @@ type Dynamics interface {
|
||||
|
||||
// 发送列车控制消息
|
||||
SendTrainControlMessage(b []byte)
|
||||
|
||||
SendTrainControl(cm *message.TrainControlMsg)
|
||||
}
|
||||
|
||||
var _default *dynamics
|
||||
@ -329,5 +331,11 @@ func (d *dynamics) sendTurnoutStateTask(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (d *dynamics) SendTrainControlMessage(b []byte) {
|
||||
d.trainControlUdpClient.Send(b)
|
||||
if d.trainControlUdpClient != nil {
|
||||
d.trainControlUdpClient.Send(b)
|
||||
}
|
||||
|
||||
}
|
||||
func (d *dynamics) SendTrainControl(cm *message.TrainControlMsg) {
|
||||
d.SendTrainControlMessage(cm.Encode())
|
||||
}
|
||||
|
55
third_party/message/train_control.go
vendored
55
third_party/message/train_control.go
vendored
@ -4,12 +4,67 @@ import (
|
||||
"encoding/binary"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 接收到的列车控制信息
|
||||
type TrainControlMsg struct {
|
||||
ControlInfo *state_proto.TrainVobcState
|
||||
TrainId string
|
||||
}
|
||||
|
||||
func (r *TrainControlMsg) Encode() []byte {
|
||||
data := make([]byte, 0)
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(r.ControlInfo.LifeSignal))
|
||||
var b2 byte
|
||||
b2 = setBit(b2, 0, IsTrue(r.ControlInfo.Tc1Active))
|
||||
b2 = setBit(b2, 1, IsTrue(r.ControlInfo.Tc2Active))
|
||||
b2 = setBit(b2, 2, IsTrue(r.ControlInfo.DirectionForward))
|
||||
b2 = setBit(b2, 3, IsTrue(r.ControlInfo.DirectionBackward))
|
||||
b2 = setBit(b2, 4, IsTrue(r.ControlInfo.TractionStatus))
|
||||
b2 = setBit(b2, 5, IsTrue(r.ControlInfo.BrakingStatus))
|
||||
b2 = setBit(b2, 6, IsTrue(r.ControlInfo.EmergencyBrakingStatus))
|
||||
b2 = setBit(b2, 7, IsTrue(r.ControlInfo.TurnbackStatus))
|
||||
var b3 byte
|
||||
b3 = setBit(b3, 0, IsTrue(r.ControlInfo.JumpStatus))
|
||||
b3 = setBit(b3, 1, IsTrue(r.ControlInfo.Ato))
|
||||
b3 = setBit(b3, 2, IsTrue(r.ControlInfo.Fam))
|
||||
b3 = setBit(b3, 3, IsTrue(r.ControlInfo.Cam))
|
||||
b3 = setBit(b3, 4, IsTrue(r.ControlInfo.TractionSafetyCircuit))
|
||||
b3 = setBit(b3, 5, IsTrue(r.ControlInfo.ParkingBrakeStatus))
|
||||
b3 = setBit(b3, 6, IsTrue(r.ControlInfo.MaintainBrakeStatus))
|
||||
data = append(data, b2)
|
||||
data = append(data, b3)
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(r.ControlInfo.TractionForce))
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(r.ControlInfo.BrakeForce))
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(r.ControlInfo.TrainLoad))
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(0)) //预留
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(0)) //预留
|
||||
data = append(data, 0) //预留
|
||||
var b4 byte
|
||||
|
||||
b4 = setBit(b4, 0, IsTrue(r.ControlInfo.LeftDoorOpenCommand))
|
||||
b4 = setBit(b4, 1, IsTrue(r.ControlInfo.RightDoorOpenCommand))
|
||||
b4 = setBit(b4, 2, IsTrue(r.ControlInfo.LeftDoorCloseCommand))
|
||||
b4 = setBit(b4, 3, IsTrue(r.ControlInfo.RightDoorCloseCommand))
|
||||
b4 = setBit(b4, 4, IsTrue(r.ControlInfo.AllDoorClose))
|
||||
data = append(data, b4)
|
||||
data = binary.BigEndian.AppendUint16(data, uint16(0)) //预留
|
||||
data = append(data, 0) //预留
|
||||
data = append(data, 0) //预留
|
||||
ti, _ := strconv.Atoi(r.TrainId)
|
||||
data = append(data, byte(ti))
|
||||
return data
|
||||
}
|
||||
func setBit(b byte, bitNum int, value byte) byte {
|
||||
if value != 0 && value != 1 {
|
||||
panic("value must be 0 or 1")
|
||||
}
|
||||
mask := byte(1 << uint(bitNum)) // 创建掩码
|
||||
b &= ^(mask) // 清除位
|
||||
b |= (mask & (value << uint(bitNum)))
|
||||
return b
|
||||
}
|
||||
|
||||
// 解析VOBC列车信息
|
||||
|
191
third_party/message/train_pc_sim_message.go
vendored
Normal file
191
third_party/message/train_pc_sim_message.go
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/snksoft/crc"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const PC_SIM_HEADER = 0xEB
|
||||
|
||||
type TrainPcSimBaseMessage struct {
|
||||
Type uint16
|
||||
DataLen byte
|
||||
Data []byte
|
||||
Crc uint16 // 校验码
|
||||
}
|
||||
|
||||
// 编码
|
||||
func (tp *TrainPcSimBaseMessage) Encode() []byte {
|
||||
pack := make([]byte, 0)
|
||||
pack = append(pack, PC_SIM_HEADER)
|
||||
pack = binary.BigEndian.AppendUint16(pack, tp.Type)
|
||||
pack = append(pack, byte(len(tp.Data)))
|
||||
dataBufs := bytes.NewBuffer(nil)
|
||||
binary.Write(dataBufs, binary.BigEndian, tp.Data)
|
||||
data := dataBufs.Bytes()
|
||||
pack = append(pack, data...)
|
||||
|
||||
pack = binary.BigEndian.AppendUint16(pack, uint16(crc.CalculateCRC(crc.CRC16, data)))
|
||||
return pack
|
||||
}
|
||||
|
||||
// 解码
|
||||
func (tp *TrainPcSimBaseMessage) Decode(data []byte) error {
|
||||
if len(data) < 3 {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
h, _ := buf.ReadByte()
|
||||
if h != PC_SIM_HEADER {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
var pcType uint16
|
||||
binary.Read(buf, binary.BigEndian, &pcType)
|
||||
len, _ := buf.ReadByte()
|
||||
if buf.Len() < int(len)+2 {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
|
||||
var dd = make([]byte, len)
|
||||
|
||||
binary.Read(buf, binary.BigEndian, &dd)
|
||||
var crcCode uint16
|
||||
binary.Read(buf, binary.BigEndian, &crcCode)
|
||||
tp.Type = pcType
|
||||
tp.DataLen = len
|
||||
tp.Data = dd
|
||||
tp.Crc = crcCode
|
||||
return nil
|
||||
//crc.CalculateCRC(crc.CRC16, data)
|
||||
}
|
||||
|
||||
func IsTrue(d bool) byte {
|
||||
if d {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func IsTrueForByte(d byte) bool {
|
||||
if d == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 列车速度位置报告
|
||||
type TrainSpeedPlaceReportMsg struct {
|
||||
//列车id
|
||||
TrainId string
|
||||
PulseCount1 uint32
|
||||
PulseCount2 uint32
|
||||
}
|
||||
|
||||
func (tp *TrainSpeedPlaceReportMsg) ParsePulseCount1(s1, s2 uint32) {
|
||||
tp.PulseCount1 += s1
|
||||
tp.PulseCount2 += s2
|
||||
}
|
||||
func (tp *TrainSpeedPlaceReportMsg) Encode(runDir bool, s1, s2 uint32) []byte {
|
||||
data := make([]byte, 0)
|
||||
binary.BigEndian.AppendUint16(data, uint16(IsTrue(runDir)))
|
||||
binary.BigEndian.AppendUint32(data, s1)
|
||||
binary.BigEndian.AppendUint32(data, s2)
|
||||
binary.BigEndian.AppendUint32(data, tp.PulseCount1)
|
||||
binary.BigEndian.AppendUint32(data, tp.PulseCount2)
|
||||
now := time.Now().UTC()
|
||||
// 将时间转换为毫秒
|
||||
millis := now.UnixNano() / int64(time.Millisecond)
|
||||
millisStr := strconv.Itoa(int(millis))
|
||||
strs := []rune(millisStr)
|
||||
|
||||
second, _ := strconv.Atoi(string(strs[:len(strs)-3]))
|
||||
mm, _ := strconv.Atoi(string(strs[len(strs)-3:]))
|
||||
binary.BigEndian.AppendUint32(data, uint32(second))
|
||||
binary.BigEndian.AppendUint16(data, uint16(mm))
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// 轨旁向列车pc仿真发送的命令码
|
||||
const (
|
||||
//钥匙开关状态
|
||||
KEY_STATE = iota
|
||||
//手柄向前控制
|
||||
HANDLE_FORWORD
|
||||
//手柄向后控制
|
||||
HANDLE_BACKWORD
|
||||
//外部紧急制动反馈
|
||||
OUTER_EMERGENCY_BRAKE
|
||||
//列车制动状态
|
||||
TRAIN_BRAKE_STATE
|
||||
//开左门
|
||||
LEFT_OPEN_DOOR
|
||||
//关右门
|
||||
CLOSE_RIGHT_DOOR
|
||||
//关左门
|
||||
CLOSE_LEFT_DOOR
|
||||
//开右门
|
||||
OPEN_RIGHT_DOOR
|
||||
//折返按钮
|
||||
TURN_BACK
|
||||
//强制门允许
|
||||
FORCE_DOOR_ALLOW
|
||||
//模式降级按钮
|
||||
TRAIN_MODE_DOWN
|
||||
|
||||
//确认按钮
|
||||
CONFIRM
|
||||
//模式升级按钮
|
||||
TRAIN_MODE_UP
|
||||
//牵引制动手柄零位
|
||||
HANDLE_TO_ZERO
|
||||
//ATO发车按钮
|
||||
ATO_SEND_TRAIN
|
||||
|
||||
//列车完整性
|
||||
TRAIN_INTEGRITY
|
||||
//车载ATP/ATO旁路状态
|
||||
ATP_ATO_BYPASS_STATe
|
||||
//车辆牵引已切除状态
|
||||
TRAIN_TRACTION_CUTED
|
||||
//障碍物检测按钮
|
||||
OBSTACLE_CHECK
|
||||
//驾驶室激活反馈按钮
|
||||
DRIVER_ACTIVE_REPORT
|
||||
//制动重故障按钮
|
||||
BRAKE_HEAVY_FAULT
|
||||
//左门状态按钮
|
||||
LEFT_DOOR_STATE
|
||||
//右门状态按钮
|
||||
RIGHT_DOOR_STATE
|
||||
//唤醒按钮
|
||||
WAKE_UP
|
||||
//检修按钮
|
||||
OVERHAUL
|
||||
//欠压按钮
|
||||
UNDERVOLTAGE
|
||||
//休眠按钮
|
||||
SLEEP
|
||||
_
|
||||
//紧急手柄拉下
|
||||
EMERGENT_HANDLE_DOWN
|
||||
//车门锁闭状态
|
||||
DOOR_LOCK_STATE
|
||||
//逃生门状态
|
||||
LIFE_DOOR
|
||||
//车辆低压上电状态
|
||||
TRAIN_LOW_POWER
|
||||
//ATP上电按钮
|
||||
ATP_POWER_ON
|
||||
_
|
||||
//AA自动开关门
|
||||
DOOR_MODE_AA
|
||||
|
||||
//AM自开人关
|
||||
DOOR_MODE_AM
|
||||
//MM人开人关
|
||||
DOOR_MODE_MM
|
||||
)
|
135
third_party/train_pc_sim/example/main.go
vendored
Normal file
135
third_party/train_pc_sim/example/main.go
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/train_pc_sim"
|
||||
"log/slog"
|
||||
"net"
|
||||
)
|
||||
|
||||
type TcpConnHandler = func(conn net.Conn)
|
||||
type TcpMsgHandler = func(n int, data []byte)
|
||||
|
||||
func tcpRunAcceptTask(listen net.Listener, port int, connHandler TcpConnHandler, msgHandler TcpMsgHandler) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
slog.Error(fmt.Sprintf("TCP服务端[port:%d]接收连接任务异常:", port), err)
|
||||
tcpRunAcceptTask(listen, port, connHandler, msgHandler)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
conn, err := listen.Accept()
|
||||
if err != nil {
|
||||
slog.Error(fmt.Sprintf("TCP服务端[port:%d]接收连接出错:", port), err)
|
||||
}
|
||||
connHandler(conn)
|
||||
tcpRunReadTask(conn, port, msgHandler)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func tcpRunReadTask(conn net.Conn, port int, msgHandler TcpMsgHandler) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
slog.Error(fmt.Sprintf("TCP服务端[port:%d]读数据任务异常:", port), err)
|
||||
tcpRunReadTask(conn, port, msgHandler)
|
||||
}
|
||||
}()
|
||||
for {
|
||||
data := make([]byte, 1024)
|
||||
l, err := conn.Read(data)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
slog.Warn(fmt.Sprintf("TCP服务端[port:%d]断开[%s]连接:", port, conn.RemoteAddr().String()))
|
||||
break
|
||||
}
|
||||
slog.Error(fmt.Sprintf("TCP服务端[port:%d]读取[%s]数据出错:", port, conn.RemoteAddr().String()), err)
|
||||
serConn = nil
|
||||
}
|
||||
msgHandler(l, data)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func StartTcpServer(port int, connHandler TcpConnHandler, msgHandler TcpMsgHandler) (net.Listener, error) {
|
||||
listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tcpRunAcceptTask(listen, port, connHandler, msgHandler)
|
||||
return listen, err
|
||||
}
|
||||
|
||||
var serConn net.Conn
|
||||
|
||||
func createServer(h TcpMsgHandler) {
|
||||
StartTcpServer(5600, func(conn net.Conn) {
|
||||
fmt.Println("TCP服务端接收到连接")
|
||||
serConn = conn
|
||||
}, h)
|
||||
}
|
||||
func connTrain() *message.TrainPcSimBaseMessage {
|
||||
msg := &message.TrainPcSimBaseMessage{}
|
||||
msg.Type = train_pc_sim.RECIVE_TRAIN_CREATE_REMOVE
|
||||
msg.Data = []byte{0x01}
|
||||
return msg
|
||||
}
|
||||
func changeDoorMode() *message.TrainPcSimBaseMessage {
|
||||
msg := &message.TrainPcSimBaseMessage{}
|
||||
msg.Type = train_pc_sim.RECIVE_TRAIN_DOOR_MODE
|
||||
msg.Data = []byte{0x02}
|
||||
return msg
|
||||
}
|
||||
|
||||
// 测试创建连接
|
||||
/*func TestConn(t *testing.T) {
|
||||
createServer(func(n int, data []byte) {
|
||||
|
||||
})
|
||||
select {}
|
||||
}*/
|
||||
|
||||
func main() {
|
||||
|
||||
createServer(func(n int, data []byte) {
|
||||
msg := &message.TrainPcSimBaseMessage{}
|
||||
msg.Decode(data)
|
||||
if msg.Type == train_pc_sim.SENDER_TRAIN_TC_ACTIVE {
|
||||
fmt.Println("接收驾驶端激活")
|
||||
} else if msg.Type == train_pc_sim.SENDER_TRAIN_TC_NOT_ACTIVE {
|
||||
fmt.Println("接收驾驶端未激活")
|
||||
} else if msg.Type == train_pc_sim.SENDER_TRAIN_OUTR_INFO {
|
||||
fmt.Println("接受列车输出数字量", msg.Data[0], msg.Data[1])
|
||||
}
|
||||
|
||||
})
|
||||
//reader := bufio.NewReader(os.Stdin)
|
||||
var command string
|
||||
for {
|
||||
if serConn == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Scanln(&command)
|
||||
if command != "" {
|
||||
|
||||
fmt.Println(command)
|
||||
}
|
||||
|
||||
if command == "create-train" {
|
||||
msg := connTrain()
|
||||
serConn.Write(msg.Encode())
|
||||
} else if command == "door-mode" {
|
||||
msg := changeDoorMode()
|
||||
serConn.Write(msg.Encode())
|
||||
}
|
||||
command = ""
|
||||
/*content, _ := reader.ReadString('\n')
|
||||
if content == "create-train" {
|
||||
}*/
|
||||
}
|
||||
select {}
|
||||
}
|
92
third_party/train_pc_sim/train_pc_sim.go
vendored
92
third_party/train_pc_sim/train_pc_sim.go
vendored
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/config"
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
"joylink.club/bj-rtsts-server/third_party/tcp"
|
||||
"joylink.club/ecs"
|
||||
"log/slog"
|
||||
@ -12,17 +13,31 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type TrainControlEvent struct {
|
||||
Command byte
|
||||
Status byte
|
||||
}
|
||||
|
||||
var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]()
|
||||
|
||||
type TrainPcSim interface {
|
||||
Start(pcSimManage TrainPcSimManage)
|
||||
Start(wd ecs.World, pcSimManage TrainPcSimManage)
|
||||
Stop()
|
||||
//发送驾驶端激活
|
||||
SendDriverActive(tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState)
|
||||
//发送牵引制动手柄
|
||||
SendHandleSwitch(oldTraction, oldBrakeForce int64, tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState)
|
||||
//发送应答器信息数据
|
||||
SendBaliseData(msgType uint16, data []byte)
|
||||
//发布列车控制的相关事件
|
||||
PublishTrainControlEvent(world ecs.World, events []TrainControlEvent)
|
||||
|
||||
CreateOrRemoveSpeedPLace(train *state_proto.TrainState)
|
||||
}
|
||||
|
||||
type TrainPcSimManage interface {
|
||||
GetTrainPcSimConfig() config.VehiclePCSimConfig
|
||||
GetConnTrain() *state_proto.TrainState
|
||||
//4.4.1. 车载输出数字量信息报文内容
|
||||
TrainPcSimDigitalOutInfoHandle(data []byte)
|
||||
//4.4.2. 车载输出数字反馈量信息报文内容
|
||||
@ -31,8 +46,9 @@ type TrainPcSimManage interface {
|
||||
TrainPcSimConnOrRemoveHandle(state byte)
|
||||
//门模式
|
||||
TrainDoorModeHandle(state byte)
|
||||
//处理列车pc仿真模拟量数据
|
||||
TrainPcSimMockInfo(data []byte)
|
||||
|
||||
//处理列车btm查询
|
||||
TrainBtmQuery(data []byte)
|
||||
}
|
||||
|
||||
@ -54,16 +70,17 @@ type trainPcSimService struct {
|
||||
pcSimClient *tcp.TcpClient
|
||||
cancleContext context.CancelFunc
|
||||
trainPcSimManage TrainPcSimManage
|
||||
speedPlace *message.TrainSpeedPlaceReportMsg
|
||||
}
|
||||
|
||||
func (pc *trainPcSimService) Start(pcSimManage TrainPcSimManage) {
|
||||
func (pc *trainPcSimService) Start(wd ecs.World, pcSimManage TrainPcSimManage) {
|
||||
config := pcSimManage.GetTrainPcSimConfig()
|
||||
if config.PcSimIp == "" || !config.Open {
|
||||
slog.Info("车载pc仿真配置未开启")
|
||||
return
|
||||
}
|
||||
|
||||
client, err := tcp.StartTcpClient(fmt.Sprintf("%v:%v", config.PcSimIp, config.PcSimPort), pc.reivceData)
|
||||
client, err := tcp.StartTcpClient(fmt.Sprintf("%v:%v", config.PcSimIp, config.PcSimPort), pc.reivceData)
|
||||
if err != nil {
|
||||
slog.Error("连接车载pc平台失败", err)
|
||||
return
|
||||
@ -72,8 +89,8 @@ func (pc *trainPcSimService) Start(pcSimManage TrainPcSimManage) {
|
||||
ctx, ctxFun := context.WithCancel(context.Background())
|
||||
pc.cancleContext = ctxFun
|
||||
pc.trainPcSimManage = pcSimManage
|
||||
//vs := pcSimManage.(*memory.VerifySimulation)
|
||||
//FireTrainControlEventType.Subscribe(vs.World, pc.trainControlEventHandle)
|
||||
|
||||
FireTrainControlEventType.Subscribe(wd, pc.trainControlEventHandle)
|
||||
|
||||
go pc.sendTrainLocationAndSpeedTask(ctx)
|
||||
}
|
||||
@ -82,7 +99,19 @@ func (pc *trainPcSimService) Stop() {
|
||||
pc.cancleContext()
|
||||
pc.cancleContext = nil
|
||||
}
|
||||
pc.pcSimClient.Close()
|
||||
if pc.pcSimClient != nil {
|
||||
pc.pcSimClient.Close()
|
||||
|
||||
}
|
||||
}
|
||||
func (pc *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) {
|
||||
if train.ConnState.Conn {
|
||||
train.PluseCount = &state_proto.SensorSpeedPulseCount{}
|
||||
pc.speedPlace = &message.TrainSpeedPlaceReportMsg{TrainId: train.Id}
|
||||
} else {
|
||||
train.PluseCount = nil
|
||||
pc.speedPlace = nil
|
||||
}
|
||||
}
|
||||
|
||||
// 依据文档80ms发送列车速度位置
|
||||
@ -93,19 +122,31 @@ func (pc *trainPcSimService) sendTrainLocationAndSpeedTask(ctx context.Context)
|
||||
return
|
||||
default:
|
||||
}
|
||||
/* trainStatus := acc.radarVobcManager.FindAccTrain()
|
||||
if trainStatus != nil {
|
||||
speedAcc := trainStatus.DynamicState.Acceleration
|
||||
t := speedAcc / accSpeedUnit
|
||||
acc.vobcClient.SendMsg(&message.Accelerometer{Acc: math.Float32bits(t)})
|
||||
}*/
|
||||
train := pc.trainPcSimManage.GetConnTrain()
|
||||
if train != nil && train.ConnState.Conn {
|
||||
s1, s2 := train.PluseCount.PulseCount1, train.PluseCount.PulseCount2
|
||||
pc.speedPlace.ParsePulseCount1(s1, s2)
|
||||
data := pc.speedPlace.Encode(train.RunDirection, s1, s2)
|
||||
bm := &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_LOCATION_INFO, Data: data}
|
||||
train.PluseCount.PulseCount1 = 0
|
||||
train.PluseCount.PulseCount2 = 0
|
||||
pc.pcSimClient.Send(bm.Encode())
|
||||
} else {
|
||||
m1 := "未找到对应连接车载pc仿真的列车"
|
||||
if train != nil {
|
||||
m1 = fmt.Sprintf("对应的列车没有连接上车载pc仿真,列车ID:%v", train.Id)
|
||||
}
|
||||
slog.Info(m1)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 80)
|
||||
}
|
||||
}
|
||||
|
||||
// 发送驾驶激活
|
||||
func (pc *trainPcSimService) SendDriverActive(tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) {
|
||||
if tc.Conn && tc.ConnType == state_proto.TrainConnState_PC_SIM {
|
||||
defulatBuf := make([]byte, 0)
|
||||
msg := &TrainPcSimBaseMessage{Data: defulatBuf}
|
||||
msg := &message.TrainPcSimBaseMessage{Data: defulatBuf}
|
||||
if vobc.Tc1Active || vobc.Tc2Active {
|
||||
msg.Type = SENDER_TRAIN_TC_ACTIVE
|
||||
} else if vobc.Tc1Active == false && vobc.Tc2Active == false {
|
||||
@ -116,7 +157,7 @@ func (pc *trainPcSimService) SendDriverActive(tc *state_proto.TrainConnState, vo
|
||||
}
|
||||
func (pc *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) {
|
||||
if tc.Conn && tc.ConnType == state_proto.TrainConnState_PC_SIM {
|
||||
msg := &TrainPcSimBaseMessage{}
|
||||
msg := &message.TrainPcSimBaseMessage{}
|
||||
newTraction := vobc.TractionForce
|
||||
newBrake := vobc.BrakeForce
|
||||
if newTraction <= oldTraction && newTraction == 0 {
|
||||
@ -140,14 +181,15 @@ func (pc *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64,
|
||||
}
|
||||
|
||||
func (pc *trainPcSimService) SendBaliseData(msgType uint16, data []byte) {
|
||||
msg := &TrainPcSimBaseMessage{}
|
||||
msg := &message.TrainPcSimBaseMessage{}
|
||||
msg.Type = msgType
|
||||
msg.Data = data
|
||||
pc.pcSimClient.Send(msg.Encode())
|
||||
}
|
||||
|
||||
func (pc *trainPcSimService) trainControlEventHandle(w ecs.World, event TrainControlEvent) {
|
||||
msg := &TrainPcSimBaseMessage{}
|
||||
fmt.Println(event.Status)
|
||||
msg := &message.TrainPcSimBaseMessage{}
|
||||
msg.Type = SENDER_TRAIN_OUTR_INFO
|
||||
data := []byte{event.Command, event.Status}
|
||||
msg.Data = data
|
||||
@ -155,7 +197,7 @@ func (pc *trainPcSimService) trainControlEventHandle(w ecs.World, event TrainCon
|
||||
}
|
||||
func (pc *trainPcSimService) PublishTrainControlEvent(world ecs.World, events []TrainControlEvent) {
|
||||
if len(events) <= 0 {
|
||||
slog.Warn("")
|
||||
slog.Warn("发布事件数量为空")
|
||||
return
|
||||
}
|
||||
for _, event := range events {
|
||||
@ -165,7 +207,7 @@ func (pc *trainPcSimService) PublishTrainControlEvent(world ecs.World, events []
|
||||
|
||||
// 接受来自pc仿真的消息
|
||||
func (pc *trainPcSimService) reivceData(len int, data []byte) {
|
||||
baseMsg := &TrainPcSimBaseMessage{}
|
||||
baseMsg := &message.TrainPcSimBaseMessage{}
|
||||
err := baseMsg.Decode(data)
|
||||
if err != nil {
|
||||
slog.Error("车载pc仿真接受数据解析失败 ")
|
||||
@ -174,17 +216,17 @@ func (pc *trainPcSimService) reivceData(len int, data []byte) {
|
||||
|
||||
switch baseMsg.Type {
|
||||
case RECIVE_TRAIN_CREATE_REMOVE:
|
||||
pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(data[0])
|
||||
pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0])
|
||||
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR:
|
||||
pc.trainPcSimManage.TrainPcSimDigitalOutInfoHandle(data)
|
||||
pc.trainPcSimManage.TrainPcSimDigitalOutInfoHandle(baseMsg.Data)
|
||||
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK:
|
||||
pc.trainPcSimManage.TrainPcSimDigitalReportHandle(data)
|
||||
pc.trainPcSimManage.TrainPcSimDigitalReportHandle(baseMsg.Data)
|
||||
case RECIVE_TRAIN_QUERY_STATUS:
|
||||
pc.trainPcSimManage.TrainBtmQuery(data)
|
||||
pc.trainPcSimManage.TrainBtmQuery(baseMsg.Data)
|
||||
case RECIVE_TRAIN_MOCK_DATA:
|
||||
pc.trainPcSimManage.TrainPcSimMockInfo(data)
|
||||
pc.trainPcSimManage.TrainPcSimMockInfo(baseMsg.Data)
|
||||
case RECIVE_TRAIN_DOOR_MODE:
|
||||
pc.trainPcSimManage.TrainDoorModeHandle(data[0])
|
||||
pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0])
|
||||
}
|
||||
|
||||
}
|
||||
|
95
third_party/train_pc_sim/train_pc_sim_message.go
vendored
95
third_party/train_pc_sim/train_pc_sim_message.go
vendored
@ -1,95 +0,0 @@
|
||||
package train_pc_sim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/snksoft/crc"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
)
|
||||
|
||||
const PC_SIM_HEADER = 0xEB
|
||||
|
||||
type TrainPcSimBaseMessage struct {
|
||||
Type uint16
|
||||
DataLen byte
|
||||
Data []byte
|
||||
Crc uint16 // 校验码
|
||||
}
|
||||
|
||||
// 编码
|
||||
func (tp *TrainPcSimBaseMessage) Encode() []byte {
|
||||
pack := make([]byte, 0)
|
||||
pack = append(pack, PC_SIM_HEADER)
|
||||
pack = binary.BigEndian.AppendUint16(pack, tp.Type)
|
||||
pack = append(pack, byte(len(tp.Data)))
|
||||
dataBufs := bytes.NewBuffer(tp.Data)
|
||||
binary.Write(dataBufs, binary.BigEndian, tp.Data)
|
||||
data := dataBufs.Bytes()
|
||||
pack = append(pack, data...)
|
||||
pack = binary.BigEndian.AppendUint16(pack, uint16(crc.CalculateCRC(crc.CRC16, data)))
|
||||
return pack
|
||||
}
|
||||
|
||||
// 解码
|
||||
func (tp *TrainPcSimBaseMessage) Decode(data []byte) error {
|
||||
if len(data) < 3 {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
buf := bytes.NewBuffer(data)
|
||||
h, _ := buf.ReadByte()
|
||||
if h != PC_SIM_HEADER {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
var pcType uint16
|
||||
binary.Read(buf, binary.BigEndian, &pcType)
|
||||
len, _ := buf.ReadByte()
|
||||
if buf.Len() < int(len)+2 {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
var dd = make([]byte, 0, len)
|
||||
binary.Read(buf, binary.BigEndian, &dd)
|
||||
var crcCode uint16
|
||||
binary.Read(buf, binary.BigEndian, &crcCode)
|
||||
tp.Type = pcType
|
||||
tp.DataLen = len
|
||||
tp.Data = dd
|
||||
tp.Crc = crcCode
|
||||
return nil
|
||||
//crc.CalculateCRC(crc.CRC16, data)
|
||||
}
|
||||
|
||||
func IsTrue(d bool) byte {
|
||||
if d {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func IsTrueForByte(d byte) bool {
|
||||
if d == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 列车pc仿真btm查询
|
||||
type TrainPcSimBtmQueryFrame struct {
|
||||
message.CanetFrame //这里不是用 can 网络的前2帧
|
||||
}
|
||||
|
||||
// func (pc *TrainPcSimBtmQueryFrame) GetCanFrameId() message.CanFrameId {
|
||||
// return pc.CanId
|
||||
// }
|
||||
//
|
||||
// func (pc *TrainPcSimBtmQueryFrame) GetCanFrameData() []byte {
|
||||
// return pc.CanData
|
||||
// }
|
||||
func (q *TrainPcSimBtmQueryFrame) Decode(d []byte) error {
|
||||
q.CanId.ID1 = d[0]
|
||||
q.CanId.ID2 = d[1]
|
||||
q.CanId.ID3 = d[2]
|
||||
q.CanId.ID4 = d[3] >> 3
|
||||
//
|
||||
q.CanData = d[4:]
|
||||
return nil
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package train_pc_sim
|
||||
|
||||
import (
|
||||
"joylink.club/ecs"
|
||||
)
|
||||
|
||||
const (
|
||||
//钥匙开关状态
|
||||
KEY_STATE = iota
|
||||
//手柄向前控制
|
||||
HANDLE_FORWORD
|
||||
//手柄向后控制
|
||||
HANDLE_BACKWORD
|
||||
//外部紧急制动反馈
|
||||
OUTER_EMERGENCY_BRAKE
|
||||
//列车制动状态
|
||||
TRAIN_BRAKE_STATE
|
||||
//开左门
|
||||
LEFT_OPEN_DOOR
|
||||
//关右门
|
||||
CLOSE_RIGHT_DOOR
|
||||
//关左门
|
||||
CLOSE_LEFT_DOOR
|
||||
//开右门
|
||||
OPEN_RIGHT_DOOR
|
||||
//折返按钮
|
||||
TURN_BACK
|
||||
//强制门允许
|
||||
FORCE_DOOR_ALLOW
|
||||
//模式降级按钮
|
||||
TRAIN_MODE_DOWN
|
||||
|
||||
//确认按钮
|
||||
CONFIRM
|
||||
//模式升级按钮
|
||||
TRAIN_MODE_UP
|
||||
//牵引制动手柄零位
|
||||
HANDLE_TO_ZERO
|
||||
//ATO发车按钮
|
||||
ATO_SEND_TRAIN
|
||||
|
||||
//列车完整性
|
||||
TRAIN_INTEGRITY
|
||||
//车载ATP/ATO旁路状态
|
||||
ATP_ATO_BYPASS_STATe
|
||||
//车辆牵引已切除状态
|
||||
TRAIN_TRACTION_CUTED
|
||||
//障碍物检测按钮
|
||||
OBSTACLE_CHECK
|
||||
//驾驶室激活反馈按钮
|
||||
DRIVER_ACTIVE_REPORT
|
||||
//制动重故障按钮
|
||||
BRAKE_HEAVY_FAULT
|
||||
//左门状态按钮
|
||||
LEFT_DOOR_STATE
|
||||
//右门状态按钮
|
||||
RIGHT_DOOR_STATE
|
||||
//唤醒按钮
|
||||
WAKE_UP
|
||||
//检修按钮
|
||||
OVERHAUL
|
||||
//欠压按钮
|
||||
UNDERVOLTAGE
|
||||
//休眠按钮
|
||||
SLEEP
|
||||
_
|
||||
//紧急手柄拉下
|
||||
EMERGENT_HANDLE_DOWN
|
||||
//车门锁闭状态
|
||||
DOOR_LOCK_STATE
|
||||
//逃生门状态
|
||||
LIFE_DOOR
|
||||
//车辆低压上电状态
|
||||
TRAIN_LOW_POWER
|
||||
//ATP上电按钮
|
||||
ATP_POWER_ON
|
||||
_
|
||||
//AA自动开关门
|
||||
DOOR_MODE_AA
|
||||
|
||||
//AM自开人关
|
||||
DOOR_MODE_AM
|
||||
//MM人开人关
|
||||
DOOR_MODE_MM
|
||||
)
|
||||
|
||||
type TrainControlEvent struct {
|
||||
Command byte
|
||||
Status byte
|
||||
}
|
||||
|
||||
var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]()
|
||||
|
||||
// 应答器组内序号
|
||||
// 唯一标识组内一个应答器,目前固定为1
|
||||
const PC_SIM_BALISE_NUM uint32 = 1
|
||||
|
||||
// 4.3.3. 应答器信息报文内容
|
||||
type TrainPcSimBtm struct {
|
||||
BaliseId uint32 //应答器组ID
|
||||
BaliseLocation float32 //应答器与车头发车位置的距离,单位cm
|
||||
BaliseData []byte //有数据的CAN数据报文
|
||||
}
|
@ -6,7 +6,7 @@ const (
|
||||
//车辆输出数字量信息
|
||||
SENDER_TRAIN_OUTR_INFO = 0x00
|
||||
//速度位置信息
|
||||
sender_train_location_info = 0x01
|
||||
SENDER_TRAIN_LOCATION_INFO = 0x01
|
||||
//驾驶室激活
|
||||
SENDER_TRAIN_TC_ACTIVE = 0x04
|
||||
//驾驶室未激活
|
||||
|
@ -66,9 +66,9 @@ func PublishMapVerifyStructure(graphic *dto.PublishedDto) {
|
||||
case data_proto.PictureType_IBP:
|
||||
graphicStorage := message.(*data_proto.IBPGraphicStorage)
|
||||
giUidMap.Store(graphic.ID, initIBPUid(graphicStorage))
|
||||
/* case data_proto.PictureType_TrainControlCab:
|
||||
graphicStorage := message.(*data_proto.TccGraphicStorage)
|
||||
giUidMap.Store(graphic.ID, initTccUid(graphicStorage))*/
|
||||
case data_proto.PictureType_TrainControlCab:
|
||||
graphicStorage := message.(*data_proto.TccGraphicStorage)
|
||||
giUidMap.Store(graphic.ID, initTccUid(graphicStorage))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,11 +22,11 @@ type elementIdStructure struct {
|
||||
}
|
||||
|
||||
// 列车控制
|
||||
/*type TccUidStructure struct {
|
||||
type TccUidStructure struct {
|
||||
ButtonIds map[uint32]*elementIdStructure
|
||||
Keys map[uint32]*elementIdStructure
|
||||
Handler map[uint32]*elementIdStructure
|
||||
}*/
|
||||
}
|
||||
|
||||
// 数组为Index为 common.ButtonCode, index, uid
|
||||
type StationUidStructure struct {
|
||||
@ -444,7 +444,7 @@ func fillCkmInfo(ckm *data_proto.GarageDoor, gus *StationUidStructure, city stri
|
||||
}
|
||||
|
||||
// 初始化列车控制
|
||||
/*func initTccUid(tcc *data_proto.TccGraphicStorage) *TccUidStructure {
|
||||
func initTccUid(tcc *data_proto.TccGraphicStorage) *TccUidStructure {
|
||||
tc := &TccUidStructure{ButtonIds: make(map[uint32]*elementIdStructure), Keys: make(map[uint32]*elementIdStructure), Handler: make(map[uint32]*elementIdStructure)}
|
||||
|
||||
for _, d := range tcc.TccButtons {
|
||||
@ -465,7 +465,7 @@ func fillCkmInfo(ckm *data_proto.GarageDoor, gus *StationUidStructure, city stri
|
||||
|
||||
}
|
||||
return tc
|
||||
}*/
|
||||
}
|
||||
|
||||
// 初始化继电器柜 UID
|
||||
func initRelayCabinetUid(data *data_proto.RelayCabinetGraphicStorage) *RelayUidStructure {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"joylink.club/bj-rtsts-server/service"
|
||||
"joylink.club/bj-rtsts-server/third_party/can_btm"
|
||||
"log/slog"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -32,6 +33,40 @@ func CreateMsgTrainConfig(trainId int, trainLen int64, configTrainData dto.Confi
|
||||
StopSign: int(configTrainData.StopSign)}
|
||||
}
|
||||
|
||||
func initTrainTcc(vs *VerifySimulation, runDir bool) *state_proto.TrainControlState {
|
||||
var tccGI *data_proto.TccGraphicStorage
|
||||
for _, id := range vs.MapIds {
|
||||
if QueryGiType(id) == data_proto.PictureType_TrainControlCab {
|
||||
tccGI = QueryGiData[*data_proto.TccGraphicStorage](id)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tcc := &state_proto.TrainControlState{}
|
||||
if tccGI != nil {
|
||||
for _, b := range tccGI.TccButtons {
|
||||
if b.Code == "JJZD" {
|
||||
tcc.Ebutton = &state_proto.TrainControlState_EmergentButton{Id: b.Common.Id, Passed: false}
|
||||
}
|
||||
}
|
||||
for _, b := range tccGI.TccHandles {
|
||||
if b.Code == "QYSB" {
|
||||
tcc.PushHandler = &state_proto.TrainControlState_PushHandler{Id: b.Common.Id, Val: 0}
|
||||
}
|
||||
}
|
||||
ds := make([]*state_proto.TrainControlState_DriverKeySwitch, 0)
|
||||
for _, b := range tccGI.TccKeys {
|
||||
if b.GetType() == data_proto.TccKey_driverControllerActivationClint {
|
||||
ds = append(ds, &state_proto.TrainControlState_DriverKeySwitch{Id: b.Common.Id, Val: false})
|
||||
} else if b.GetType() == data_proto.TccKey_frontAndRearDirectionalControl {
|
||||
tcc.DirKey = &state_proto.TrainControlState_DirectionKeySwitch{Id: b.Common.Id, Val: uint32(message.IsTrue(runDir))}
|
||||
}
|
||||
}
|
||||
tcc.DriverKey = ds
|
||||
}
|
||||
return tcc
|
||||
}
|
||||
|
||||
// 增加列车状态
|
||||
func AddTrainStateNew(vs *VerifySimulation, status *state_proto.TrainState, configTrainData dto.ConfigTrainData, trainEndsA dto.ConfigTrainEnds,
|
||||
trainEndsB dto.ConfigTrainEnds, mapId int32) *sys_error.BusinessError {
|
||||
@ -75,7 +110,8 @@ func AddTrainStateNew(vs *VerifySimulation, status *state_proto.TrainState, conf
|
||||
status.PointTo = pointTo
|
||||
status.TrainKilometer = kilometer.Value
|
||||
|
||||
status.Tcc = &state_proto.TrainControlState{}
|
||||
//status.Tcc = &state_proto.TrainControlState{}
|
||||
status.Tcc = initTrainTcc(vs, status.RunDirection)
|
||||
status.DynamicState = &state_proto.TrainDynamicState{
|
||||
HeadLinkId: linkId,
|
||||
HeadLinkOffset: loffset,
|
||||
@ -89,6 +125,13 @@ func AddTrainStateNew(vs *VerifySimulation, status *state_proto.TrainState, conf
|
||||
//初始化列车参数状态
|
||||
createOrUpdateStateDynamicConfig(status, configTrainData, trainEndsA, trainEndsB)
|
||||
status.VobcState = &state_proto.TrainVobcState{}
|
||||
|
||||
if status.RunDirection {
|
||||
status.VobcState.DirectionForward = true
|
||||
} else {
|
||||
status.VobcState.DirectionBackward = true
|
||||
}
|
||||
|
||||
slog.Debug("列车初始化", "trainIndex", trainIndex, "linkId", linkId, "loffset", loffset)
|
||||
linkIdInt, _ := strconv.Atoi(linkId)
|
||||
err := dynamics.Default().RequestAddTrain(&message.InitTrainInfo{
|
||||
@ -124,22 +167,34 @@ func TrainConnTypeUpdate(vs *VerifySimulation, ct *dto.TrainConnThirdDto) {
|
||||
if !ok {
|
||||
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", ct.Id)))
|
||||
}
|
||||
train := data.(*state_proto.TrainState)
|
||||
var conn = true
|
||||
if ct.ConnType == state_proto.TrainConnState_NONE {
|
||||
conn = false
|
||||
}
|
||||
if conn {
|
||||
if ct.ConnType != state_proto.TrainConnState_NONE {
|
||||
//列车连接 半实物或车载pc仿真
|
||||
allTrainMap.Range(func(k, v any) bool {
|
||||
tmpTrain := v.(*state_proto.TrainState)
|
||||
if tmpTrain.ConnState.Conn {
|
||||
panic(sys_error.New(fmt.Sprintf("列车【%s】已经连接,此列车无法联系", k)))
|
||||
connTypeName := "半实物"
|
||||
if tmpTrain.ConnState.ConnType == state_proto.TrainConnState_PC_SIM {
|
||||
connTypeName = "车载pc仿真"
|
||||
}
|
||||
panic(sys_error.New(fmt.Sprintf("列车[%s]已经连接 [%v],此列车无法连接", k, connTypeName)))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if ct.ConnType == state_proto.TrainConnState_VOBC && conn {
|
||||
//半实物直接更改连接状态
|
||||
//车载仿真有创建/删除,列车的操作,通过连接状态区分车载pc是否创建列车
|
||||
train.ConnState.Conn = true
|
||||
}
|
||||
} else {
|
||||
train.ConnState.Conn = false
|
||||
}
|
||||
train := data.(*state_proto.TrainState)
|
||||
//train.ConnState.Conn = conn
|
||||
|
||||
train.ConnState.ConnType = ct.ConnType
|
||||
}
|
||||
|
||||
@ -298,8 +353,35 @@ func UpdateTrainStateByDynamics(vs *VerifySimulation, trainId string, info *mess
|
||||
sta.DynamicState.TailRadarSpeed = speedParse(info.TailRadarSpeed)
|
||||
sta.DynamicState.Acceleration = info.Acceleration
|
||||
sta.DynamicState.Displacement = int32(info.Displacement)
|
||||
pluseCount(sta)
|
||||
return sta
|
||||
}
|
||||
|
||||
// 接受动力学时间15毫米
|
||||
const RECEIVE_DYNAMIC_DATA_RATE = 15
|
||||
|
||||
func formatSpeedTime(s int32) int32 {
|
||||
d3, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", s), 64)
|
||||
return int32(math.Abs(math.Round(d3)))
|
||||
}
|
||||
func pluseCount(sta *state_proto.TrainState) {
|
||||
if sta.PluseCount == nil {
|
||||
return
|
||||
}
|
||||
if sta.RunDirection {
|
||||
if sta.TrainEndsA.SpeedSensorEnableA {
|
||||
sta.PluseCount.PulseCount1 += uint32(formatSpeedTime(sta.DynamicState.HeadSensorSpeed1 * RECEIVE_DYNAMIC_DATA_RATE))
|
||||
} else if sta.TrainEndsA.SpeedSensorEnableB {
|
||||
sta.PluseCount.PulseCount2 += uint32(formatSpeedTime(sta.DynamicState.HeadSensorSpeed2 * RECEIVE_DYNAMIC_DATA_RATE))
|
||||
}
|
||||
} else {
|
||||
if sta.TrainEndsB.SpeedSensorEnableA {
|
||||
sta.PluseCount.PulseCount1 += uint32(formatSpeedTime(sta.DynamicState.TailSensorSpeed1 * RECEIVE_DYNAMIC_DATA_RATE))
|
||||
} else if sta.TrainEndsB.SpeedSensorEnableB {
|
||||
sta.PluseCount.PulseCount2 += uint32(formatSpeedTime(sta.DynamicState.TailSensorSpeed2 * RECEIVE_DYNAMIC_DATA_RATE))
|
||||
}
|
||||
}
|
||||
}
|
||||
func RemoveAllTrain(vs *VerifySimulation) {
|
||||
allTrainMap := &vs.Memory.Status.TrainStateMap
|
||||
if allTrainMap == nil {
|
||||
|
@ -277,18 +277,11 @@ func (s *VerifySimulation) HandleSectionCmdMsg(city string, lineId string, centr
|
||||
// 处理动力学列车速度消息
|
||||
func (s *VerifySimulation) HandleDynamicsTrainInfo(info *message.DynamicsTrainInfo) {
|
||||
trainId := strconv.Itoa(int(info.Number))
|
||||
trainData, ok := s.Memory.Status.TrainStateMap.Load(trainId)
|
||||
_, ok := s.Memory.Status.TrainStateMap.Load(trainId)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
train := trainData.(*state_proto.TrainState)
|
||||
connType := train.ConnState
|
||||
//列车连接并且是半实物连接 date 2024-3-15
|
||||
if connType.Conn && connType.ConnType == state_proto.TrainConnState_VOBC {
|
||||
// 给半实物仿真发送速度
|
||||
semi_physical_train.Default().SendTrainControlMessage(info)
|
||||
}
|
||||
|
||||
semi_physical_train.Default().SendTrainControlMessage(info)
|
||||
// 更新列车状态
|
||||
trainState := UpdateTrainStateByDynamics(s, trainId, info)
|
||||
// 更新电机转速
|
||||
@ -388,13 +381,13 @@ func (s *VerifySimulation) HandleSemiPhysicalTrainControlMsg(b []byte) {
|
||||
}
|
||||
connState := train.ConnState
|
||||
trainId, err := strconv.Atoi(train.Id)
|
||||
d := append(b, uint8(trainId))
|
||||
// 发送给动力学,不论列车是否连接三方服务都将数据发送给动力学
|
||||
dynamics.Default().SendTrainControlMessage(d)
|
||||
if err != nil {
|
||||
panic(dto.ErrorDto{Code: dto.ArgumentParseError, Message: err.Error()})
|
||||
}
|
||||
if connState.Conn == true && connState.ConnType == state_proto.TrainConnState_VOBC {
|
||||
d := append(b, uint8(trainId))
|
||||
// 发送给动力学
|
||||
dynamics.Default().SendTrainControlMessage(d)
|
||||
// 存放至列车中
|
||||
controlMessage := &message.TrainControlMsg{}
|
||||
controlMessage.Decode(b)
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||||
"joylink.club/bj-rtsts-server/sys_error"
|
||||
"joylink.club/bj-rtsts-server/third_party/can_btm"
|
||||
"joylink.club/bj-rtsts-server/third_party/dynamics"
|
||||
"joylink.club/bj-rtsts-server/third_party/message"
|
||||
train_pc_sim "joylink.club/bj-rtsts-server/third_party/train_pc_sim"
|
||||
"log/slog"
|
||||
@ -36,6 +37,7 @@ func (s *VerifySimulation) ControlTrainUpdate(ct *request_proto.TrainControl) {
|
||||
} else if ct.ControlType == request_proto.TrainControl_DRIVER_KEY_SWITCH {
|
||||
tce = trainControlDriverKey(sta, ct.DriverKey, ct.DeviceId)
|
||||
train_pc_sim.Default().SendDriverActive(sta.ConnState, sta.VobcState)
|
||||
|
||||
} else if ct.ControlType == request_proto.TrainControl_DIRECTION_KEY_SWITCH {
|
||||
trainControlDirKey(sta, ct.DirKey, ct.DeviceId)
|
||||
} else if ct.ControlType == request_proto.TrainControl_HANDLER {
|
||||
@ -45,13 +47,15 @@ func (s *VerifySimulation) ControlTrainUpdate(ct *request_proto.TrainControl) {
|
||||
train_pc_sim.Default().SendHandleSwitch(oldTraction, oldBrakeForce, sta.ConnState, sta.VobcState)
|
||||
}
|
||||
|
||||
if !sta.ConnState.Conn && sta.ConnState.ConnType != state_proto.TrainConnState_PC_SIM {
|
||||
/* if !sta.ConnState.Conn && sta.ConnState.ConnType != state_proto.TrainConnState_PC_SIM {
|
||||
slog.Error("当前列车未连接车载pc仿真,", sta.Id)
|
||||
return
|
||||
}*/
|
||||
if sta.ConnState.Conn && sta.ConnState.ConnType == state_proto.TrainConnState_PC_SIM {
|
||||
train_pc_sim.Default().PublishTrainControlEvent(s.World, tce)
|
||||
}
|
||||
|
||||
train_pc_sim.Default().PublishTrainControlEvent(s.World, tce)
|
||||
|
||||
cm := &message.TrainControlMsg{TrainId: ct.TrainId, ControlInfo: sta.VobcState}
|
||||
dynamics.Default().SendTrainControl(cm)
|
||||
}
|
||||
|
||||
func trainControlEB(trainState *state_proto.TrainState, request *request_proto.TrainControl_EmergentButton, deviceId uint32) {
|
||||
@ -67,10 +71,15 @@ func trainControlEB(trainState *state_proto.TrainState, request *request_proto.T
|
||||
func trainControlDirKey(trainState *state_proto.TrainState, request *request_proto.TrainControl_DirectionKeySwitch, deviceId uint32) {
|
||||
trainState.VobcState.DirectionForward = false
|
||||
trainState.VobcState.DirectionBackward = false
|
||||
trainState.RunDirection = false
|
||||
if request.Val == 1 {
|
||||
trainState.VobcState.DirectionForward = true
|
||||
trainState.RunDirection = true
|
||||
|
||||
} else if request.Val == 0 {
|
||||
trainState.VobcState.DirectionBackward = false
|
||||
trainState.VobcState.DirectionBackward = true
|
||||
trainState.RunDirection = false
|
||||
|
||||
}
|
||||
if trainState.Tcc.DirKey == nil {
|
||||
trainState.Tcc.DirKey = &state_proto.TrainControlState_DirectionKeySwitch{Id: deviceId, Val: request.Val}
|
||||
@ -99,7 +108,7 @@ func trainControlDriverKey(trainState *state_proto.TrainState, request *request_
|
||||
if addNew {
|
||||
trainState.Tcc.DriverKey = append(trainState.Tcc.DriverKey, &state_proto.TrainControlState_DriverKeySwitch{Id: deviceId, Val: request.Val})
|
||||
}
|
||||
return []train_pc_sim.TrainControlEvent{{Command: train_pc_sim.KEY_STATE, Status: train_pc_sim.IsTrue(request.Val)}}
|
||||
return []train_pc_sim.TrainControlEvent{{Command: message.KEY_STATE, Status: message.IsTrue(request.Val)}}
|
||||
|
||||
}
|
||||
|
||||
@ -110,20 +119,20 @@ func trainControlHandle(trainState *state_proto.TrainState, request *request_pro
|
||||
trainState.VobcState.BrakingStatus = false
|
||||
trainState.VobcState.BrakeForce = 0
|
||||
tce := make([]train_pc_sim.TrainControlEvent, 0)
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_FORWORD, Status: 0})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_BACKWORD, Status: 0})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_TO_ZERO, Status: 0})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: message.HANDLE_FORWORD, Status: 0})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: message.HANDLE_BACKWORD, Status: 0})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: message.HANDLE_TO_ZERO, Status: 0})
|
||||
if request.Val > 0 {
|
||||
trainState.VobcState.TractionStatus = true
|
||||
trainState.VobcState.TractionForce = int64(request.Val)
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_FORWORD, Status: 1})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: message.HANDLE_FORWORD, Status: 1})
|
||||
} else if request.Val < 0 {
|
||||
trainState.VobcState.BrakingStatus = true
|
||||
trainState.VobcState.BrakeForce = int64(request.Val)
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_BACKWORD, Status: 1})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: message.HANDLE_BACKWORD, Status: 1})
|
||||
} else {
|
||||
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_TO_ZERO, Status: 1})
|
||||
tce = append(tce, train_pc_sim.TrainControlEvent{Command: message.HANDLE_TO_ZERO, Status: 1})
|
||||
}
|
||||
if trainState.Tcc.PushHandler == nil {
|
||||
trainState.Tcc.PushHandler = &state_proto.TrainControlState_PushHandler{Id: deviceId, Val: request.Val}
|
||||
@ -132,6 +141,10 @@ func trainControlHandle(trainState *state_proto.TrainState, request *request_pro
|
||||
}
|
||||
return tce
|
||||
}
|
||||
|
||||
func (s *VerifySimulation) GetConnTrain() *state_proto.TrainState {
|
||||
return s.findConnTrain(state_proto.TrainConnState_PC_SIM)
|
||||
}
|
||||
func (s *VerifySimulation) findConnTrain(ct state_proto.TrainConnState_TrainConnType) *state_proto.TrainState {
|
||||
var findTrain *state_proto.TrainState
|
||||
s.Memory.Status.TrainStateMap.Range(func(k, v any) bool {
|
||||
@ -200,48 +213,50 @@ func (s *VerifySimulation) TrainPcSimDigitalOutInfoHandle(data []byte) {
|
||||
brakeQuarantine, _ := buf.ReadByte() //制动隔离
|
||||
stopNotAllBrake, _ := buf.ReadByte() //停放制动缓解
|
||||
|
||||
vobc.TractionSafetyCircuit = train_pc_sim.IsTrueForByte(cutTraction)
|
||||
vobc.TrainDoorOutLed = train_pc_sim.IsTrueForByte(trainDoorOutLed) //? 说明暂无此属性
|
||||
vobc.ParkingBrakeStatus = train_pc_sim.IsTrueForByte(stopBrakeAppend)
|
||||
vobc.EmergencyBrakingStatus = train_pc_sim.IsTrueForByte(emergentBrake)
|
||||
vobc.LeftDoorOpenCommand = train_pc_sim.IsTrueForByte(leftOpenDoor)
|
||||
vobc.RightDoorOpenCommand = train_pc_sim.IsTrueForByte(rightOpenDoor)
|
||||
vobc.RightDoorCloseCommand = train_pc_sim.IsTrueForByte(closeRightDoor)
|
||||
vobc.AllDoorClose = train_pc_sim.IsTrueForByte(doorAlwaysClosed)
|
||||
vobc.LocalAtpControl = train_pc_sim.IsTrueForByte(localAtpControl) //?
|
||||
vobc.Ato = train_pc_sim.IsTrueForByte(atoMode)
|
||||
vobc.AtoTractionCommandOut = train_pc_sim.IsTrueForByte(atoTractionCommandOut) //?
|
||||
vobc.TractionSafetyCircuit = message.IsTrueForByte(cutTraction)
|
||||
vobc.TrainDoorOutLed = message.IsTrueForByte(trainDoorOutLed) //? 说明暂无此属性
|
||||
vobc.ParkingBrakeStatus = message.IsTrueForByte(stopBrakeAppend)
|
||||
vobc.EmergencyBrakingStatus = message.IsTrueForByte(emergentBrake)
|
||||
vobc.LeftDoorOpenCommand = message.IsTrueForByte(leftOpenDoor)
|
||||
vobc.RightDoorOpenCommand = message.IsTrueForByte(rightOpenDoor)
|
||||
vobc.RightDoorCloseCommand = message.IsTrueForByte(closeRightDoor)
|
||||
vobc.AllDoorClose = message.IsTrueForByte(doorAlwaysClosed)
|
||||
vobc.LocalAtpControl = message.IsTrueForByte(localAtpControl) //?
|
||||
vobc.Ato = message.IsTrueForByte(atoMode)
|
||||
vobc.AtoTractionCommandOut = message.IsTrueForByte(atoTractionCommandOut) //?
|
||||
|
||||
vobc.AtoTractionCommand1 = train_pc_sim.IsTrueForByte(atoTractionCommand1) //?
|
||||
vobc.AtoTractionCommand2 = train_pc_sim.IsTrueForByte(atoTractionCommand2) //?
|
||||
vobc.AtoTractionCommand3 = train_pc_sim.IsTrueForByte(atoTractionCommand3) //?
|
||||
vobc.AtoBrakeCommand = train_pc_sim.IsTrueForByte(atoBrakeCommand) //?
|
||||
vobc.JumpStatus = train_pc_sim.IsTrueForByte(skipCommand)
|
||||
vobc.DirectionForward = train_pc_sim.IsTrueForByte(direction1)
|
||||
vobc.DirectionBackward = train_pc_sim.IsTrueForByte(direction2)
|
||||
vobc.AtoTractionCommand1 = message.IsTrueForByte(atoTractionCommand1) //?
|
||||
vobc.AtoTractionCommand2 = message.IsTrueForByte(atoTractionCommand2) //?
|
||||
vobc.AtoTractionCommand3 = message.IsTrueForByte(atoTractionCommand3) //?
|
||||
vobc.AtoBrakeCommand = message.IsTrueForByte(atoBrakeCommand) //?
|
||||
vobc.JumpStatus = message.IsTrueForByte(skipCommand)
|
||||
vobc.DirectionForward = message.IsTrueForByte(direction1)
|
||||
vobc.DirectionBackward = message.IsTrueForByte(direction2)
|
||||
|
||||
vobc.AtoLazyCommandOut = train_pc_sim.IsTrueForByte(atoLazyCommandOut) //?
|
||||
vobc.SleepBtn = train_pc_sim.IsTrueForByte(sleepCommand)
|
||||
vobc.WakeUpBtn = train_pc_sim.IsTrueForByte(wakeUpCommand)
|
||||
vobc.AtoSendTrainBtn = train_pc_sim.IsTrueForByte(toPullTrainLed)
|
||||
vobc.TurnbackStatus = train_pc_sim.IsTrueForByte(arLightCommand) //?
|
||||
vobc.AtoAlwaysBrake = train_pc_sim.IsTrueForByte(atoAlwaysBrake) //?
|
||||
vobc.AtoOpenLeftDoor = train_pc_sim.IsTrueForByte(atoOpenLeftDoor) //?
|
||||
vobc.AtoOpenRightDoor = train_pc_sim.IsTrueForByte(atoOpenRightDoor) //?
|
||||
vobc.AtoCloseLeftDoor = train_pc_sim.IsTrueForByte(atoCloseLeftDoor) //?
|
||||
vobc.Tc1Active = train_pc_sim.IsTrueForByte(ariverActive)
|
||||
vobc.NoSpeedSigle = train_pc_sim.IsTrueForByte(noSpeedSigle) //?
|
||||
vobc.Fam = train_pc_sim.IsTrueForByte(famMode)
|
||||
vobc.Cam = train_pc_sim.IsTrueForByte(camMode)
|
||||
vobc.TrainStartedLed = train_pc_sim.IsTrueForByte(trainStartedLed) //?
|
||||
vobc.MostUseBrake = train_pc_sim.IsTrueForByte(mostUseBrake) //? //常用制动
|
||||
vobc.SplittingOut = train_pc_sim.IsTrueForByte(splittingOut) //? //过分相输出
|
||||
vobc.ModeRelay = train_pc_sim.IsTrueForByte(modeRelay) //? //模式继电器
|
||||
vobc.TractionEffective = train_pc_sim.IsTrueForByte(tractionEffective) //? //牵引有效
|
||||
vobc.BrakeEffective = train_pc_sim.IsTrueForByte(brakeEffective) //? //制动有效
|
||||
vobc.LifeDoorState = train_pc_sim.IsTrueForByte(lifeDoorUsed) //逃生门使能
|
||||
vobc.BrakeQuarantine = train_pc_sim.IsTrueForByte(brakeQuarantine) //? //制动隔离
|
||||
vobc.StopNotAllBrake = train_pc_sim.IsTrueForByte(stopNotAllBrake) //? //停放制动缓解
|
||||
vobc.AtoLazyCommandOut = message.IsTrueForByte(atoLazyCommandOut) //?
|
||||
vobc.SleepBtn = message.IsTrueForByte(sleepCommand)
|
||||
vobc.WakeUpBtn = message.IsTrueForByte(wakeUpCommand)
|
||||
vobc.AtoSendTrainBtn = message.IsTrueForByte(toPullTrainLed)
|
||||
vobc.TurnbackStatus = message.IsTrueForByte(arLightCommand) //?
|
||||
vobc.AtoAlwaysBrake = message.IsTrueForByte(atoAlwaysBrake) //?
|
||||
vobc.AtoOpenLeftDoor = message.IsTrueForByte(atoOpenLeftDoor) //?
|
||||
vobc.AtoOpenRightDoor = message.IsTrueForByte(atoOpenRightDoor) //?
|
||||
vobc.AtoCloseLeftDoor = message.IsTrueForByte(atoCloseLeftDoor) //?
|
||||
vobc.Tc1Active = message.IsTrueForByte(ariverActive)
|
||||
vobc.NoSpeedSigle = message.IsTrueForByte(noSpeedSigle) //?
|
||||
vobc.Fam = message.IsTrueForByte(famMode)
|
||||
vobc.Cam = message.IsTrueForByte(camMode)
|
||||
vobc.TrainStartedLed = message.IsTrueForByte(trainStartedLed) //?
|
||||
vobc.MostUseBrake = message.IsTrueForByte(mostUseBrake) //? //常用制动
|
||||
vobc.SplittingOut = message.IsTrueForByte(splittingOut) //? //过分相输出
|
||||
vobc.ModeRelay = message.IsTrueForByte(modeRelay) //? //模式继电器
|
||||
vobc.TractionEffective = message.IsTrueForByte(tractionEffective) //? //牵引有效
|
||||
vobc.BrakeEffective = message.IsTrueForByte(brakeEffective) //? //制动有效
|
||||
vobc.LifeDoorState = message.IsTrueForByte(lifeDoorUsed) //逃生门使能
|
||||
vobc.BrakeQuarantine = message.IsTrueForByte(brakeQuarantine) //? //制动隔离
|
||||
vobc.StopNotAllBrake = message.IsTrueForByte(stopNotAllBrake) //? //停放制动缓解
|
||||
cm := &message.TrainControlMsg{TrainId: train.Id, ControlInfo: vobc}
|
||||
dynamics.Default().SendTrainControl(cm)
|
||||
}
|
||||
|
||||
// 4.4.2. 车载输出数字反馈量信息报文内容
|
||||
@ -261,9 +276,9 @@ func (s *VerifySimulation) TrainPcSimDigitalReportHandle(data []byte) {
|
||||
direction1, _ := buf.ReadByte()
|
||||
direction2, _ := buf.ReadByte()
|
||||
|
||||
vobc.Tc1Active = train_pc_sim.IsTrueForByte(localEndAct) //本端驾驶室激活(钥匙)
|
||||
vobc.DirectionForward = train_pc_sim.IsTrueForByte(direction1) //方向手柄进位
|
||||
vobc.DirectionBackward = train_pc_sim.IsTrueForByte(direction2) //方向手柄退位
|
||||
vobc.Tc1Active = message.IsTrueForByte(localEndAct) //本端驾驶室激活(钥匙)
|
||||
vobc.DirectionForward = message.IsTrueForByte(direction1) //方向手柄进位
|
||||
vobc.DirectionBackward = message.IsTrueForByte(direction2) //方向手柄退位
|
||||
}
|
||||
|
||||
// 创建/删除列车
|
||||
@ -279,6 +294,7 @@ func (s *VerifySimulation) TrainPcSimConnOrRemoveHandle(state byte) {
|
||||
} else {
|
||||
connState.Conn = false
|
||||
}
|
||||
train_pc_sim.Default().CreateOrRemoveSpeedPLace(train)
|
||||
}
|
||||
|
||||
// 门模式
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"joylink.club/bj-rtsts-server/third_party/acc"
|
||||
"joylink.club/bj-rtsts-server/third_party/radar"
|
||||
"joylink.club/bj-rtsts-server/third_party/train_pc_sim"
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@ -136,7 +137,7 @@ func runThirdParty(s *memory.VerifySimulation) error {
|
||||
radar.Default().Start(s)
|
||||
//列车加速计发送vobc
|
||||
acc.Default().Start(s)
|
||||
//train_pc_sim.Default().Start(s)
|
||||
train_pc_sim.Default().Start(s.World, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -162,6 +163,8 @@ func stopThirdParty(s *memory.VerifySimulation) {
|
||||
radar.Default().Stop()
|
||||
// 加速计服务停止
|
||||
acc.Default().Stop()
|
||||
//列车PC仿真停止
|
||||
train_pc_sim.Default().Stop()
|
||||
}
|
||||
|
||||
func createSimulationId(projectId int32) string {
|
||||
|
Loading…
Reference in New Issue
Block a user