Merge branch 'develop' into local-test
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m30s

This commit is contained in:
tiger_zhou 2024-05-24 09:01:24 +08:00
commit 4b9e398f67
7 changed files with 296 additions and 113 deletions

View File

@ -14,6 +14,9 @@
- viper 配置管理 - viper 配置管理
- swagger 文档生成(基于[swaggo](https://github.com/swaggo/swag))(需要安装 go install github.com/swaggo/swag/cmd/swag@latest在项目根目录即 bj-rtsts-server-go执行 swag init然后启动项目即可启动后访问 http://{ip}:{port}/swagger/index.html - swagger 文档生成(基于[swaggo](https://github.com/swaggo/swag))(需要安装 go install github.com/swaggo/swag/cmd/swag@latest在项目根目录即 bj-rtsts-server-go执行 swag init然后启动项目即可启动后访问 http://{ip}:{port}/swagger/index.html
# 动力学
- dotnet.exe publish -r win-x64 -p:PublishSingleFile=true
# 开发说明 # 开发说明
- 在 api 目录下添加路由及请求数据解析返回 - 在 api 目录下添加路由及请求数据解析返回

View File

@ -72,7 +72,8 @@ type ThridPartyConfig struct {
CidcModbus []CidcModbusConfig `json:"cidcModbus" description:"联锁驱采Modbus接口配置"` CidcModbus []CidcModbusConfig `json:"cidcModbus" description:"联锁驱采Modbus接口配置"`
Radar RadarConfig `json:"radar" description:"车载雷达相关配置"` Radar RadarConfig `json:"radar" description:"车载雷达相关配置"`
Acc AccConfig `json:"acc" description:"车载加速计"` Acc AccConfig `json:"acc" description:"车载加速计"`
PcSimConfig VehiclePCSimConfig `json:"pcSimConfig" description:"车载pc仿真平台通信"` //PcSimConfig VehiclePCSimConfig `json:"pcSimConfig" description:"车载pc仿真平台通信"`
PcSimConfigs []VehiclePCSimConfig `json:"pcSimConfigs" description:"车载pc仿真平台通信"`
} }
type RadarConfig struct { type RadarConfig struct {
Open bool `json:"open" description:"是否开启"` Open bool `json:"open" description:"是否开启"`
@ -170,8 +171,16 @@ type RsspConfig struct {
RemoteUdpPort int `json:"remoteUdpPort" description:"远程服务器端口"` //远程服务器端口 RemoteUdpPort int `json:"remoteUdpPort" description:"远程服务器端口"` //远程服务器端口
LocalUdpPort int `json:"localUdpPort" description:"本地服务器端口"` //本地服务器端口 LocalUdpPort int `json:"localUdpPort" description:"本地服务器端口"` //本地服务器端口
} }
type VehiclePCSimConfig2 struct {
TrainEnds bool `json:"trainEnds" description:"列车端点A"`
Open bool `json:"open" description:"是否开启"`
PcSimIp string `json:"pcSimIp" description:"pc仿真平台通信ip"`
PcSimPort uint32 `json:"pcSimPort" description:"pc仿真平台通信端口"`
LocalTestingPort uint32 `json:"localTestingPort" description:"本地测试端口"`
}
type VehiclePCSimConfig struct { type VehiclePCSimConfig struct {
TrainEnds bool `json:"trainEnds" description:"列车端点A?"`
Open bool `json:"open" description:"是否开启"` Open bool `json:"open" description:"是否开启"`
PcSimIp string `json:"pcSimIp" description:"pc仿真平台通信ip"` PcSimIp string `json:"pcSimIp" description:"pc仿真平台通信ip"`
PcSimPort uint32 `json:"pcSimPort" description:"pc仿真平台通信端口"` PcSimPort uint32 `json:"pcSimPort" description:"pc仿真平台通信端口"`

View File

@ -9,12 +9,14 @@ import (
) )
type TcpClient struct { type TcpClient struct {
conn *net.TCPConn conn *net.TCPConn
handler func(n int, data []byte) handler func(n int, data []byte)
ctx context.CancelFunc ctx context.CancelFunc
conning bool
properties map[string]interface{}
} }
func StartTcpClient(rAddr string, handler func(n int, data []byte), readErr func(err error)) (*TcpClient, error) { func StartTcpClient(rAddr string, properties map[string]interface{}, handler func(n int, data []byte, clientProperties map[string]interface{}), readErr func(err error)) (*TcpClient, error) {
raddr, addErr := net.ResolveTCPAddr("tcp", rAddr) raddr, addErr := net.ResolveTCPAddr("tcp", rAddr)
if addErr != nil { if addErr != nil {
return nil, addErr return nil, addErr
@ -25,6 +27,7 @@ func StartTcpClient(rAddr string, handler func(n int, data []byte), readErr func
if err != nil { if err != nil {
return nil, err return nil, err
} }
client := &TcpClient{conn: conn, ctx: ctxFun, properties: properties}
go func() { go func() {
for { for {
select { select {
@ -37,17 +40,21 @@ func StartTcpClient(rAddr string, handler func(n int, data []byte), readErr func
if err != nil { if err != nil {
if opErr, ok := err.(*net.OpError); ok { if opErr, ok := err.(*net.OpError); ok {
slog.Error(fmt.Sprintf("TCP客户端[rAddr:%s]读取数据异常连接可能断开:", rAddr), opErr) slog.Error(fmt.Sprintf("TCP客户端[rAddr:%s]读取数据异常连接可能断开:", rAddr), opErr)
client.conning = false
readErr(err) readErr(err)
} }
if err == io.EOF { if err == io.EOF {
slog.Warn(fmt.Sprintf("TCP客户端[rAddr:%s]断开连接:", rAddr)) slog.Warn(fmt.Sprintf("TCP客户端[rAddr:%s]断开连接:", rAddr))
client.conning = false
readErr(err) readErr(err)
} }
} }
handler(l, data) client.conning = true
handler(l, data, client.properties)
} }
}() }()
return &TcpClient{conn: conn, ctx: ctxFun}, nil client.conning = true
return client, nil
} }
func (c *TcpClient) Close() { func (c *TcpClient) Close() {
if c != nil && c.conn != nil { if c != nil && c.conn != nil {
@ -57,6 +64,12 @@ func (c *TcpClient) Close() {
c.conn = nil c.conn = nil
} }
} }
func (c *TcpClient) IsConning() bool {
if c != nil && c.conn != nil {
return c.conning
}
return false
}
func (c *TcpClient) Send(data []byte) error { func (c *TcpClient) Send(data []byte) error {
if c == nil || c.conn == nil { if c == nil || c.conn == nil {

View File

@ -8,9 +8,7 @@ import (
"joylink.club/bj-rtsts-server/third_party/message" "joylink.club/bj-rtsts-server/third_party/message"
"joylink.club/bj-rtsts-server/third_party/tcp" "joylink.club/bj-rtsts-server/third_party/tcp"
"joylink.club/bj-rtsts-server/third_party/tpapi" "joylink.club/bj-rtsts-server/third_party/tpapi"
"joylink.club/ecs"
"log/slog" "log/slog"
"strconv"
"sync" "sync"
"time" "time"
) )
@ -20,47 +18,57 @@ type TrainControlEvent struct {
Status byte Status byte
} }
var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]() //var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]()
type TrainPcSim interface { type TrainPcSim interface {
tpapi.ThirdPartyApiService tpapi.ThirdPartyApiService
Start(pcSimManage TrainPcSimManage) Start(pcSimManage TrainPcSimManage)
Stop() Stop()
// SendDriverActive 发送驾驶端激活 // SendDriverActive 发送驾驶端激活
SendDriverActive(tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) SendDriverActive(train *state_proto.TrainState)
// SendHandleSwitch 发送牵引制动手柄 // SendHandleSwitch 发送牵引制动手柄
SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState)
// SendTrainDirection 列车运行方向 // SendTrainDirection 列车运行方向
//因文档说明不清楚,在调用的时候目前是注释状态,现场调试可能会用到 //因文档说明不清楚,在调用的时候目前是注释状态,现场调试可能会用到
SendTrainDirection(trainForward, trainBackward bool) SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool)
//发送应答器信息数据 //发送应答器信息数据
SendBaliseData(msgType uint16, data []byte) SendBaliseData(train *state_proto.TrainState, msgType uint16, data []byte)
//发布列车控制的相关事件 //发布列车控制的相关事件
PublishTrainControlEvent(events []TrainControlEvent) PublishTrainControlEvent(train *state_proto.TrainState, events []TrainControlEvent)
// CreateOrRemoveSpeedPLace 创建或删除速度位置信息 // CreateOrRemoveSpeedPLace 创建或删除速度位置信息
CreateOrRemoveSpeedPLace(train *state_proto.TrainState) CreateOrRemoveSpeedPLace(train *state_proto.TrainState)
// CreateOrRemoveTrain 创建或删除列车 // CreateOrRemoveTrain 创建或删除列车
CreateOrRemoveTrain(msgType byte, data []byte) error CreateOrRemoveTrain(train *state_proto.TrainState, msgType byte, data []byte) error
} }
type TrainPcSimManage interface { type TrainPcSimManage interface {
GetTrainPcSimConfig() config.VehiclePCSimConfig GetTrainPcSimConfig() []config.VehiclePCSimConfig
GetConnTrain() *state_proto.TrainState //GetConnTrain() *state_proto.TrainState
GetConnTrain2() []*state_proto.TrainState
// TrainPcSimDigitalOutInfoHandle 4.4.1. 车载输出数字量信息报文内容 // TrainPcSimDigitalOutInfoHandle 4.4.1. 车载输出数字量信息报文内容
TrainPcSimDigitalOutInfoHandle(data []byte) TrainPcSimDigitalOutInfoHandle(connType state_proto.TrainConnState_TrainConnType, data []byte)
// TrainPcSimDigitalReportHandle 4.4.2. 车载输出数字反馈量信息报文内容 // TrainPcSimDigitalReportHandle 4.4.2. 车载输出数字反馈量信息报文内容
TrainPcSimDigitalReportHandle(data []byte) TrainPcSimDigitalReportHandle(connType state_proto.TrainConnState_TrainConnType, data []byte)
// TrainPcSimMockInfo 门模式 // TrainPcSimMockInfo 门模式
//TrainDoorModeHandle(state byte) //TrainDoorModeHandle(state byte)
//处理列车pc仿真模拟量数据 //处理列车pc仿真模拟量数据
TrainPcSimMockInfo(data []byte) TrainPcSimMockInfo(connType state_proto.TrainConnState_TrainConnType, data []byte)
// TrainBtmQuery 处理列车btm查询 // TrainBtmQuery 处理列车btm查询
TrainBtmQuery(data []byte) TrainBtmQuery(connType state_proto.TrainConnState_TrainConnType, data []byte)
} }
const Name = "车载pc仿真" const Name = "车载pc仿真"
const CLIENT_KEY = "clientKey"
func FindTrainPcSimClientKey(t *state_proto.TrainState) string {
if t.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A {
return "A"
} else if t.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B {
return "B"
}
return ""
}
func (d *trainPcSimService) Name() string { func (d *trainPcSimService) Name() string {
return Name return Name
} }
@ -86,80 +94,149 @@ func Default() TrainPcSim {
} }
type trainPcSimService struct { type trainPcSimService struct {
state tpapi.ThirdPartyApiServiceState state tpapi.ThirdPartyApiServiceState
pcSimClient *tcp.TcpClient //pcSimClient *tcp.TcpClient
pcSimClientMap map[string]*tcp.TcpClient
cancleContext context.CancelFunc cancleContext context.CancelFunc
trainPcSimManage TrainPcSimManage trainPcSimManage TrainPcSimManage
speedPlace *message.TrainSpeedPlaceReportMsg speedPlace *message.TrainSpeedPlaceReportMsg
config config.VehiclePCSimConfig configs []config.VehiclePCSimConfig
} }
// 接受来自pc仿真的消息 // 接受来自pc仿真的消息
func (d *trainPcSimService) readError(err error) { func (d *trainPcSimService) readError(err error) {
slog.Error("连接车载pc仿真tcp服务断开", err) slog.Error("连接车载pc仿真tcp服务断开", err)
d.updateState(tpapi.ThirdPartyState_Broken) d.updateState(tpapi.ThirdPartyState_Broken)
d.pcSimClient.Close()
d.connTrainPcSim()
} }
func (d *trainPcSimService) connTrainPcSim() {
reconnIndex := 0 func (d *trainPcSimService) closeAllConn() {
ctx, ctxFun := context.WithCancel(context.Background()) for key, client := range d.pcSimClientMap {
if client != nil {
client.Close()
}
delete(d.pcSimClientMap, key)
}
}
func (d *trainPcSimService) closeConn(clientKey string) {
if d.pcSimClientMap[clientKey] != nil {
d.pcSimClientMap[clientKey].Close()
delete(d.pcSimClientMap, clientKey)
}
}
func (d *trainPcSimService) findConfig(tcChar string) (*config.VehiclePCSimConfig, error) {
configFlag := false
if tcChar == "A" {
configFlag = true
} else if tcChar == "B" {
configFlag = false
} else {
return nil, fmt.Errorf("")
}
for _, config := range d.configs {
if config.Open && config.TrainEnds == configFlag {
return &config, nil
}
}
return nil, fmt.Errorf("")
}
func (d *trainPcSimService) connTrainPcSim(ctx context.Context) {
//reconnIndex := 0
//ctx, ctxFun := context.WithCancel(context.Background())
go func() { go func() {
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
d.closeAllConn()
return return
default: default:
} }
d.pcSimClient.Close() trains := d.trainPcSimManage.GetConnTrain2()
client, err := tcp.StartTcpClient(fmt.Sprintf("%v:%v", d.config.PcSimIp, d.config.PcSimPort), d.reivceData, d.readError) if len(trains) > 0 {
if err != nil { for _, t := range trains {
reconnIndex++ clientKey := FindTrainPcSimClientKey(t)
d.updateState(tpapi.ThirdPartyState_Broken) if clientKey == "" {
if reconnIndex%10 == 0 { slog.Error("未找到对应的pc仿真连接,trainId:", t.Id, "删除对应客户端")
slog.Error("连接车载pc平台失败尝试=", strconv.Itoa(reconnIndex), err) d.closeConn(clientKey)
continue
}
client := d.pcSimClientMap[clientKey]
if !client.IsConning() {
client.Close()
d.initConn(clientKey)
}
} }
} else {
d.pcSimClient = client
ctxFun()
return
} }
time.Sleep(time.Second) time.Sleep(time.Second)
} }
}() }()
} }
func (d *trainPcSimService) initConn(clientKey string) {
client := d.pcSimClientMap[clientKey]
if d.pcSimClientMap[clientKey] == nil {
client = &tcp.TcpClient{}
d.pcSimClientMap[clientKey] = client
}
config, _ := d.findConfig(clientKey)
addr := fmt.Sprintf("%v:%v", config.PcSimIp, config.PcSimPort)
properties := map[string]interface{}{
CLIENT_KEY: clientKey,
}
fmt.Println(properties[CLIENT_KEY])
client2, err := tcp.StartTcpClient(addr, properties, d.reivceData, d.readError)
if err != nil {
d.updateState(tpapi.ThirdPartyState_Broken)
} else {
d.pcSimClientMap[clientKey] = client2
}
}
func (d *trainPcSimService) Start(pcSimManage TrainPcSimManage) { func (d *trainPcSimService) Start(pcSimManage TrainPcSimManage) {
config := pcSimManage.GetTrainPcSimConfig() configs := pcSimManage.GetTrainPcSimConfig()
if config.PcSimIp == "" || !config.Open { d.pcSimClientMap = map[string]*tcp.TcpClient{}
if len(configs) <= 0 {
slog.Info("车载pc仿真配置未开启") slog.Info("车载pc仿真配置未开启")
return return
} }
d.config = config allClosed := true
d.connTrainPcSim() for _, c := range configs {
if !c.Open {
allClosed = false
}
}
if !allClosed {
slog.Info("车载pc仿真配置未开启")
return
}
d.configs = configs
ctx, ctxFun := context.WithCancel(context.Background()) ctx, ctxFun := context.WithCancel(context.Background())
d.cancleContext = ctxFun d.cancleContext = ctxFun
d.trainPcSimManage = pcSimManage d.trainPcSimManage = pcSimManage
d.connTrainPcSim(ctx)
//FireTrainControlEventType.Subscribe(wd, d.trainControlEventHandle) //FireTrainControlEventType.Subscribe(wd, d.trainControlEventHandle)
d.updateState(tpapi.ThirdPartyState_Normal) d.updateState(tpapi.ThirdPartyState_Normal)
go d.sendTrainLocationAndSpeedTask(ctx) go d.sendTrainLocationAndSpeedTask(ctx)
} }
func (d *trainPcSimService) Stop() { func (d *trainPcSimService) Stop() {
d.updateState(tpapi.ThirdPartyState_Closed) d.updateState(tpapi.ThirdPartyState_Closed)
if d.cancleContext != nil { if d.cancleContext != nil {
d.cancleContext() d.cancleContext()
d.cancleContext = nil d.cancleContext = nil
} }
if d.pcSimClient != nil { d.closeAllConn()
//d.pcSimClient.
d.pcSimClient.Close()
}
} }
func (d *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) { func (d *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) {
if train.ConnState.Conn { if train.ConnState.Conn && (train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A || train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B) {
train.PluseCount = &state_proto.SensorSpeedPulseCount{} train.PluseCount = &state_proto.SensorSpeedPulseCount{}
d.speedPlace = &message.TrainSpeedPlaceReportMsg{TrainId: train.Id} d.speedPlace = &message.TrainSpeedPlaceReportMsg{TrainId: train.Id}
} else { } else {
@ -167,10 +244,20 @@ func (d *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainSta
d.speedPlace = nil d.speedPlace = nil
} }
} }
func (d *trainPcSimService) CreateOrRemoveTrain(msgType byte, data []byte) error { func (d *trainPcSimService) CreateOrRemoveTrain(train *state_proto.TrainState, msgType byte, data []byte) error {
msg := &message.TrainPcSimBaseMessage{Data: data, Type: uint16(msgType)} clientKey := FindTrainPcSimClientKey(train)
return d.pcSimClient.Send(msg.Encode()) if msgType == RECIVE_TRAIN_CREATE_REMOVE && data[0] == 0x01 {
d.initConn(clientKey)
}
msg := &message.TrainPcSimBaseMessage{Data: data, Type: uint16(msgType)}
client := d.pcSimClientMap[clientKey]
err := client.Send(msg.Encode())
if data[0] != 0x01 {
d.closeConn(clientKey)
}
return err
} }
// 依据文档80ms发送列车速度位置 // 依据文档80ms发送列车速度位置
@ -181,36 +268,57 @@ func (d *trainPcSimService) sendTrainLocationAndSpeedTask(ctx context.Context) {
return return
default: default:
} }
train := d.trainPcSimManage.GetConnTrain() trains := d.trainPcSimManage.GetConnTrain2()
if train != nil && train.ConnState.Conn && train.PluseCount != nil { for _, train := range trains {
if train.ConnState.Conn && train.PluseCount != nil {
clientKey := FindTrainPcSimClientKey(train)
client := d.pcSimClientMap[clientKey]
s1, s2 := train.PluseCount.PulseCount1, train.PluseCount.PulseCount2
d.speedPlace.ParsePulseCount1(s1, s2)
data := d.speedPlace.Encode(train.TrainRunUp, s1, s2)
bm := &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_LOCATION_INFO, Data: data}
train.PluseCount.PulseCount1 = 0
train.PluseCount.PulseCount2 = 0
client.Send(bm.Encode())
}
s1, s2 := train.PluseCount.PulseCount1, train.PluseCount.PulseCount2
d.speedPlace.ParsePulseCount1(s1, s2)
data := d.speedPlace.Encode(train.TrainRunUp, s1, s2)
bm := &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_LOCATION_INFO, Data: data}
train.PluseCount.PulseCount1 = 0
train.PluseCount.PulseCount2 = 0
d.pcSimClient.Send(bm.Encode())
} }
time.Sleep(time.Millisecond * 80) time.Sleep(time.Millisecond * 80)
} }
} }
// 发送驾驶激活 // 发送驾驶激活
func (d *trainPcSimService) SendDriverActive(tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) { func (d *trainPcSimService) SendDriverActive(train *state_proto.TrainState) {
if tc.Conn && tc.ConnType == state_proto.TrainConnState_PC_SIM { vobc := train.VobcState
defulatBuf := make([]byte, 0)
msg := &message.TrainPcSimBaseMessage{Data: defulatBuf} clientKey := FindTrainPcSimClientKey(train)
if vobc.Tc1Active || vobc.Tc2Active { client := d.pcSimClientMap[clientKey]
defulatBuf := make([]byte, 0)
msg := &message.TrainPcSimBaseMessage{Data: defulatBuf}
if train.TrainRunUp {
if vobc.Tc1Active {
msg.Type = SENDER_TRAIN_TC_ACTIVE msg.Type = SENDER_TRAIN_TC_ACTIVE
} else if vobc.Tc1Active == false && vobc.Tc2Active == false { } else if vobc.Tc1Active == false {
msg.Type = SENDER_TRAIN_TC_NOT_ACTIVE
}
} else if !train.TrainRunUp {
if vobc.Tc2Active {
msg.Type = SENDER_TRAIN_TC_ACTIVE
} else if vobc.Tc2Active == false {
msg.Type = SENDER_TRAIN_TC_NOT_ACTIVE msg.Type = SENDER_TRAIN_TC_NOT_ACTIVE
} }
d.pcSimClient.Send(msg.Encode())
} }
client.Send(msg.Encode())
} }
func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, tc *state_proto.TrainConnState, vobc *state_proto.TrainVobcState) { func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState) {
if tc.Conn && tc.ConnType == state_proto.TrainConnState_PC_SIM { tc := train.ConnState
if tc.Conn {
vobc := train.VobcState
clientKey := FindTrainPcSimClientKey(train)
client := d.pcSimClientMap[clientKey]
msg := &message.TrainPcSimBaseMessage{} msg := &message.TrainPcSimBaseMessage{}
newTraction := vobc.TractionForce newTraction := vobc.TractionForce
newBrake := -vobc.BrakeForce newBrake := -vobc.BrakeForce
@ -239,10 +347,10 @@ func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, t
msg.Type = RECIVE_TRAIN_HAND_KEY_BACKWARD msg.Type = RECIVE_TRAIN_HAND_KEY_BACKWARD
} }
} }
d.pcSimClient.Send(msg.Encode()) client.Send(msg.Encode())
} }
} }
func (d *trainPcSimService) SendTrainDirection(trainForward, trainBackward bool) { func (d *trainPcSimService) SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool) {
baseMsgs := make([]*message.TrainPcSimBaseMessage, 0) baseMsgs := make([]*message.TrainPcSimBaseMessage, 0)
if !trainForward && !trainBackward { if !trainForward && !trainBackward {
@ -253,18 +361,22 @@ func (d *trainPcSimService) SendTrainDirection(trainForward, trainBackward bool)
} else if trainBackward { } else if trainBackward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_BACKWARD}) baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_BACKWARD})
} }
clientKey := FindTrainPcSimClientKey(train)
client := d.pcSimClientMap[clientKey]
for _, msg := range baseMsgs { for _, msg := range baseMsgs {
d.pcSimClient.Send(msg.Encode()) client.Send(msg.Encode())
} }
} }
func (d *trainPcSimService) SendBaliseData(msgType uint16, data []byte) { func (d *trainPcSimService) SendBaliseData(train *state_proto.TrainState, msgType uint16, data []byte) {
msg := &message.TrainPcSimBaseMessage{} msg := &message.TrainPcSimBaseMessage{}
msg.Type = msgType msg.Type = msgType
msg.Data = data msg.Data = data
clientKey := FindTrainPcSimClientKey(train)
client := d.pcSimClientMap[clientKey]
//fmt.Println(fmt.Sprintf("%X", msg.Encode())) //fmt.Println(fmt.Sprintf("%X", msg.Encode()))
d.pcSimClient.Send(msg.Encode()) client.Send(msg.Encode())
} }
/* /*
@ -276,24 +388,37 @@ func (d *trainPcSimService) SendBaliseData(msgType uint16, data []byte) {
d.pcSimClient.Send(msg.Encode()) d.pcSimClient.Send(msg.Encode())
} }
*/ */
func (d *trainPcSimService) PublishTrainControlEvent(events []TrainControlEvent) { func (d *trainPcSimService) PublishTrainControlEvent(train *state_proto.TrainState, events []TrainControlEvent) {
if len(events) <= 0 { if len(events) <= 0 {
slog.Warn("发布事件数量为空") slog.Warn("发布事件数量为空")
return return
} }
clientKey := FindTrainPcSimClientKey(train)
client := d.pcSimClientMap[clientKey]
for _, event := range events { for _, event := range events {
msg := &message.TrainPcSimBaseMessage{} msg := &message.TrainPcSimBaseMessage{}
msg.Type = SENDER_TRAIN_OUTR_INFO msg.Type = SENDER_TRAIN_OUTR_INFO
data := []byte{event.Command, event.Status} data := []byte{event.Command, event.Status}
msg.Data = data msg.Data = data
d.pcSimClient.Send(msg.Encode()) client.Send(msg.Encode())
//FireTrainControlEventType.Publish(world, &event) //FireTrainControlEventType.Publish(world, &event)
} }
} }
// 接受来自pc仿真的消息 // 接受来自pc仿真的消息
func (d *trainPcSimService) reivceData(len int, data []byte) { func (d *trainPcSimService) reivceData(len int, data []byte, properties map[string]interface{}) {
clientKey := properties[CLIENT_KEY]
ck := fmt.Sprintf("%v", clientKey)
if d.pcSimClientMap[ck] == nil {
slog.Error(fmt.Sprintf("不存在%v的客户端,数据解析不予处理", ck))
return
}
connType := state_proto.TrainConnState_PC_SIM_A
if clientKey == "B" {
connType = state_proto.TrainConnState_PC_SIM_B
}
baseMsg := &message.TrainPcSimBaseMessage{} baseMsg := &message.TrainPcSimBaseMessage{}
err := baseMsg.Decode(data) err := baseMsg.Decode(data)
if err != nil { if err != nil {
@ -305,13 +430,13 @@ func (d *trainPcSimService) reivceData(len int, data []byte) {
//case RECIVE_TRAIN_CREATE_REMOVE: //case RECIVE_TRAIN_CREATE_REMOVE:
// pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0]) // pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0])
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR: case RECIVE_TRAIN_INTERFACE_CABINET_OUTR:
d.trainPcSimManage.TrainPcSimDigitalOutInfoHandle(baseMsg.Data) d.trainPcSimManage.TrainPcSimDigitalOutInfoHandle(connType, baseMsg.Data)
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK: case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK:
d.trainPcSimManage.TrainPcSimDigitalReportHandle(baseMsg.Data) d.trainPcSimManage.TrainPcSimDigitalReportHandle(connType, baseMsg.Data)
case RECIVE_TRAIN_QUERY_STATUS: case RECIVE_TRAIN_QUERY_STATUS:
d.trainPcSimManage.TrainBtmQuery(baseMsg.Data) d.trainPcSimManage.TrainBtmQuery(connType, baseMsg.Data)
case RECIVE_TRAIN_MOCK_DATA: case RECIVE_TRAIN_MOCK_DATA:
d.trainPcSimManage.TrainPcSimMockInfo(baseMsg.Data) d.trainPcSimManage.TrainPcSimMockInfo(connType, baseMsg.Data)
//case RECIVE_TRAIN_DOOR_MODE: //case RECIVE_TRAIN_DOOR_MODE:
// pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0]) // pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0])
} }

View File

@ -151,8 +151,10 @@ func TrainConnTypeUpdate(vs *VerifySimulation, ct *dto.TrainConnThirdDto) {
tmpTrain := v.(*state_proto.TrainState) tmpTrain := v.(*state_proto.TrainState)
if tmpTrain.ConnState.Conn { if tmpTrain.ConnState.Conn {
connTypeName := "半实物" connTypeName := "半实物"
if tmpTrain.ConnState.ConnType == state_proto.TrainConnState_PC_SIM { if tmpTrain.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A {
connTypeName = "车载pc仿真" connTypeName = "车载pc仿真-A"
} else if tmpTrain.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A {
connTypeName = "车载pc仿真-B"
} }
panic(sys_error.New(fmt.Sprintf("列车[%s]已经连接 [%v],此列车无法连接", k, connTypeName))) panic(sys_error.New(fmt.Sprintf("列车[%s]已经连接 [%v],此列车无法连接", k, connTypeName)))
return false return false
@ -162,14 +164,14 @@ func TrainConnTypeUpdate(vs *VerifySimulation, ct *dto.TrainConnThirdDto) {
} }
train.ConnState.Conn = true train.ConnState.Conn = true
train.ConnState.ConnType = ct.ConnType train.ConnState.ConnType = ct.ConnType
if ct.ConnType == state_proto.TrainConnState_PC_SIM { if ct.ConnType == state_proto.TrainConnState_PC_SIM_A || ct.ConnType == state_proto.TrainConnState_PC_SIM_B {
err := TrainPcSimConnOrRemoveHandle(train) err := TrainPcSimConnOrRemoveHandle(train)
if err != nil { if err != nil {
train.ConnState.Conn = false train.ConnState.Conn = false
train.ConnState.ConnType = state_proto.TrainConnState_NONE train.ConnState.ConnType = state_proto.TrainConnState_NONE
panic(sys_error.New(err.Error())) panic(sys_error.New(err.Error()))
} }
train_pc_sim.Default().SendDriverActive(train.ConnState, train.VobcState) train_pc_sim.Default().SendDriverActive(train)
} }
} }
@ -184,8 +186,8 @@ func TrainUnConn(vs *VerifySimulation, trainId string) {
train := data.(*state_proto.TrainState) train := data.(*state_proto.TrainState)
oldType := train.ConnState.ConnType oldType := train.ConnState.ConnType
train.ConnState.Conn = false train.ConnState.Conn = false
train.ConnState.ConnType = state_proto.TrainConnState_NONE //train.ConnState.ConnType = state_proto.TrainConnState_NONE
if oldType == state_proto.TrainConnState_PC_SIM { if oldType == state_proto.TrainConnState_PC_SIM_A || oldType == state_proto.TrainConnState_PC_SIM_B {
err := TrainPcSimConnOrRemoveHandle(train) err := TrainPcSimConnOrRemoveHandle(train)
if err != nil { if err != nil {
panic(sys_error.New("未连接车载PC仿真无法断开连接")) panic(sys_error.New("未连接车载PC仿真无法断开连接"))

View File

@ -494,14 +494,25 @@ func (s *VerifySimulation) CollectInterlockRelayInfo(code string) *message.Inter
} }
return nil return nil
} }
// 获取列车可用连接半实物类型
func (s *VerifySimulation) FindTrainConnTypes() []dto.TrainConnTypeConfigDto { func (s *VerifySimulation) FindTrainConnTypes() []dto.TrainConnTypeConfigDto {
typeConfig := make([]dto.TrainConnTypeConfigDto, 0) typeConfig := make([]dto.TrainConnTypeConfigDto, 0)
if s.runConfig.Vobc.Open /*&& s.runConfig.Vobc.Ip != ""*/ { if s.runConfig.Vobc.Open /*&& s.runConfig.Vobc.Ip != ""*/ {
typeConfig = append(typeConfig, dto.TrainConnTypeConfigDto{ConnType: state_proto.TrainConnState_VOBC}) typeConfig = append(typeConfig, dto.TrainConnTypeConfigDto{ConnType: state_proto.TrainConnState_VOBC})
} }
if s.runConfig.PcSimConfig.Open /*&& s.runConfig.PcSimConfig.PcSimIp != ""*/ {
typeConfig = append(typeConfig, dto.TrainConnTypeConfigDto{ConnType: state_proto.TrainConnState_PC_SIM}) for _, pcSim := range s.runConfig.PcSimConfigs {
dto := dto.TrainConnTypeConfigDto{ConnType: state_proto.TrainConnState_PC_SIM_A}
if !pcSim.TrainEnds {
dd := &dto
dd.ConnType = state_proto.TrainConnState_PC_SIM_B
}
typeConfig = append(typeConfig, dto)
} }
/* if s.runConfig.PcSimConfig.Open {
typeConfig = append(typeConfig, dto.TrainConnTypeConfigDto{ConnType: state_proto.TrainConnState_PC_SIM})
}*/
return typeConfig return typeConfig
} }

View File

@ -18,8 +18,8 @@ import (
"time" "time"
) )
func (s *VerifySimulation) GetTrainPcSimConfig() config.VehiclePCSimConfig { func (s *VerifySimulation) GetTrainPcSimConfig() []config.VehiclePCSimConfig {
return s.runConfig.PcSimConfig return s.runConfig.PcSimConfigs
} }
// 列车控制 // 列车控制
@ -42,7 +42,7 @@ func ControlTrainUpdate(s *VerifySimulation, ct *request_proto.TrainControl) {
tce = trainControlEB(vobc, tcc, ct.Button, ct.DeviceId, tccGraphicData) tce = trainControlEB(vobc, tcc, ct.Button, ct.DeviceId, tccGraphicData)
} else if ct.ControlType == request_proto.TrainControl_DRIVER_KEY_SWITCH { } else if ct.ControlType == request_proto.TrainControl_DRIVER_KEY_SWITCH {
tce = trainControlDriverKey(sta, ct.DriverKey, ct.DeviceId, tccGraphicData) tce = trainControlDriverKey(sta, ct.DriverKey, ct.DeviceId, tccGraphicData)
train_pc_sim.Default().SendDriverActive(sta.ConnState, sta.VobcState) train_pc_sim.Default().SendDriverActive(sta)
} else if ct.ControlType == request_proto.TrainControl_DIRECTION_KEY_SWITCH { } else if ct.ControlType == request_proto.TrainControl_DIRECTION_KEY_SWITCH {
tce = trainControlDirKey(sta.DynamicState.Speed, vobc, tcc, ct.DirKey, ct.DeviceId, tccGraphicData) tce = trainControlDirKey(sta.DynamicState.Speed, vobc, tcc, ct.DirKey, ct.DeviceId, tccGraphicData)
//此处先注释,根据现场调试情况 2024-4-16 //此处先注释,根据现场调试情况 2024-4-16
@ -52,7 +52,7 @@ func ControlTrainUpdate(s *VerifySimulation, ct *request_proto.TrainControl) {
oldBrakeForce := sta.VobcState.BrakeForce oldBrakeForce := sta.VobcState.BrakeForce
isTraction := ct.Handler.Val > 0 //是否制动 isTraction := ct.Handler.Val > 0 //是否制动
tce = trainControlHandle(vobc, tcc, ct.Handler, ct.DeviceId, tccGraphicData) tce = trainControlHandle(vobc, tcc, ct.Handler, ct.DeviceId, tccGraphicData)
train_pc_sim.Default().SendHandleSwitch(oldTraction, oldBrakeForce, isTraction, sta.ConnState, sta.VobcState) train_pc_sim.Default().SendHandleSwitch(oldTraction, oldBrakeForce, isTraction, sta)
} }
if !vobc.DirectionForward && !vobc.DirectionBackward { if !vobc.DirectionForward && !vobc.DirectionBackward {
@ -62,8 +62,8 @@ func ControlTrainUpdate(s *VerifySimulation, ct *request_proto.TrainControl) {
if vobc.EmergencyBrakingStatus { if vobc.EmergencyBrakingStatus {
vobc.TractionForce = 0 vobc.TractionForce = 0
} }
if sta.ConnState.Conn && sta.ConnState.ConnType == state_proto.TrainConnState_PC_SIM && tce != nil { if sta.ConnState.Conn && (sta.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A || sta.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B) && tce != nil {
train_pc_sim.Default().PublishTrainControlEvent(tce) train_pc_sim.Default().PublishTrainControlEvent(sta, tce)
} }
} }
@ -226,8 +226,27 @@ func trainControlHandle(vobc *state_proto.TrainVobcState, tcc *state_proto.Train
return tce return tce
} }
func (s *VerifySimulation) GetConnTrain() *state_proto.TrainState { /*func (s *VerifySimulation) GetConnTrain() *state_proto.TrainState {
return s.findConnTrain(state_proto.TrainConnState_PC_SIM) return s.findConnTrain(state_proto.TrainConnState_PC_SIM)
}*/
func (s *VerifySimulation) GetConnTrain2() []*state_proto.TrainState {
return s.findConnTrain2(state_proto.TrainConnState_PC_SIM_A, state_proto.TrainConnState_PC_SIM_B)
}
func (s *VerifySimulation) findConnTrain2(ct1, ct2 state_proto.TrainConnState_TrainConnType) []*state_proto.TrainState {
var trains = make([]*state_proto.TrainState, 0)
s.Memory.Status.TrainStateMap.Range(func(k, v any) bool {
train := v.(*state_proto.TrainState)
if train.Show {
connState := train.ConnState
if connState.ConnType == ct1 || connState.ConnType == ct2 {
trains = append(trains, train)
}
}
return true
})
return trains
} }
func (s *VerifySimulation) findConnTrain(ct state_proto.TrainConnState_TrainConnType) *state_proto.TrainState { func (s *VerifySimulation) findConnTrain(ct state_proto.TrainConnState_TrainConnType) *state_proto.TrainState {
var findTrain *state_proto.TrainState var findTrain *state_proto.TrainState
@ -244,8 +263,8 @@ func (s *VerifySimulation) findConnTrain(ct state_proto.TrainConnState_TrainConn
} }
// 4.4.1. 车载输出数字量信息报文内容 // 4.4.1. 车载输出数字量信息报文内容
func (s *VerifySimulation) TrainPcSimDigitalOutInfoHandle(data []byte) { func (s *VerifySimulation) TrainPcSimDigitalOutInfoHandle(connType state_proto.TrainConnState_TrainConnType, data []byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM) train := s.findConnTrain(connType)
if train == nil { if train == nil {
slog.Error("车载输出数字量未找到连接车载pc仿真的列车") slog.Error("车载输出数字量未找到连接车载pc仿真的列车")
return return
@ -407,8 +426,8 @@ func trainPcSimDigitalOutInfoHandleCode7_0(d byte, vobc *state_proto.TrainVobcSt
} }
// 4.4.2. 车载输出数字反馈量信息报文内容 // 4.4.2. 车载输出数字反馈量信息报文内容
func (s *VerifySimulation) TrainPcSimDigitalReportHandle(data []byte) { func (s *VerifySimulation) TrainPcSimDigitalReportHandle(connType state_proto.TrainConnState_TrainConnType, data []byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM) train := s.findConnTrain(connType)
if train == nil { if train == nil {
slog.Error("车载输出数字反馈量信息,未找到连接车载pc仿真的列车") slog.Error("车载输出数字反馈量信息,未找到连接车载pc仿真的列车")
return return
@ -439,8 +458,9 @@ func TrainPcSimConnOrRemoveHandle(train *state_proto.TrainState) error {
if train.ConnState.Conn == false { if train.ConnState.Conn == false {
data = 0x00 data = 0x00
} }
if train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM { if train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_A || train.ConnState.ConnType == state_proto.TrainConnState_PC_SIM_B {
crErr := train_pc_sim.Default().CreateOrRemoveTrain(train_pc_sim.RECIVE_TRAIN_CREATE_REMOVE, []byte{data})
crErr := train_pc_sim.Default().CreateOrRemoveTrain(train, train_pc_sim.RECIVE_TRAIN_CREATE_REMOVE, []byte{data})
if crErr != nil { if crErr != nil {
return crErr return crErr
} }
@ -469,8 +489,8 @@ func TrainPcSimConnOrRemoveHandle(train *state_proto.TrainState) error {
}*/ }*/
// 4.4.3. 车载输出模拟量信息报文内容(0x03) // 4.4.3. 车载输出模拟量信息报文内容(0x03)
func (s *VerifySimulation) TrainPcSimMockInfo(data []byte) { func (s *VerifySimulation) TrainPcSimMockInfo(connType state_proto.TrainConnState_TrainConnType, data []byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM) train := s.findConnTrain(connType)
if train == nil { if train == nil {
slog.Error("车载输出模拟量,未找到连接车载pc仿真的列车") slog.Error("车载输出模拟量,未找到连接车载pc仿真的列车")
return return
@ -484,12 +504,12 @@ func (s *VerifySimulation) TrainPcSimMockInfo(data []byte) {
} }
// 4.4.4. 车载输出BTM查询同步帧报文内容0x04 // 4.4.4. 车载输出BTM查询同步帧报文内容0x04
func (s *VerifySimulation) TrainBtmQuery(data []byte) { func (s *VerifySimulation) TrainBtmQuery(connType state_proto.TrainConnState_TrainConnType, data []byte) {
if len(data) < 12 { if len(data) < 12 {
slog.Error("列车btm查询报文长度错误:", len(data)) slog.Error("列车btm查询报文长度错误:", len(data))
return return
} }
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM) train := s.findConnTrain(connType)
if train == nil { if train == nil {
slog.Error("车载输出btm查询,未找到连接车载pc仿真的列车") slog.Error("车载输出btm查询,未找到连接车载pc仿真的列车")
return return
@ -511,7 +531,7 @@ func (s *VerifySimulation) TrainBtmQuery(data []byte) {
//重新发送 //重新发送
if len(train.BtmState.BaliseTelegramForPcSimResend) > 0 { if len(train.BtmState.BaliseTelegramForPcSimResend) > 0 {
dd, _ := hex.DecodeString(train.BtmState.BaliseTelegramForPcSimResend) dd, _ := hex.DecodeString(train.BtmState.BaliseTelegramForPcSimResend)
train_pc_sim.Default().SendBaliseData(train_pc_sim.RECIVE_TRAIN_BTM_HAS_DATA, dd) train_pc_sim.Default().SendBaliseData(train, train_pc_sim.RECIVE_TRAIN_BTM_HAS_DATA, dd)
} }
} else { } else {
timeSyncF := message.NewBtmTimeSyncCheckFrame(trainAtm.CanId.ID4, true) timeSyncF := message.NewBtmTimeSyncCheckFrame(trainAtm.CanId.ID4, true)
@ -523,7 +543,7 @@ func (s *VerifySimulation) TrainBtmQuery(data []byte) {
queryData := make([]byte, 0) queryData := make([]byte, 0)
queryData = append(queryData, btmRepFrame.Encode().Encode()...) queryData = append(queryData, btmRepFrame.Encode().Encode()...)
queryData = append(queryData, timeSyncF.Encode().Encode()...) queryData = append(queryData, timeSyncF.Encode().Encode()...)
train_pc_sim.Default().SendBaliseData(train_pc_sim.RECIVE_TRAIN_BTM_NOT_DATA, queryData) train_pc_sim.Default().SendBaliseData(train, train_pc_sim.RECIVE_TRAIN_BTM_NOT_DATA, queryData)
} else { } else {
//有数据 //有数据
aliseData, _ := hex.DecodeString(train.BtmState.Telegram) aliseData, _ := hex.DecodeString(train.BtmState.Telegram)
@ -534,7 +554,7 @@ func (s *VerifySimulation) TrainBtmQuery(data []byte) {
queryData = append(queryData, timeSyncF.Encode().Encode()...) queryData = append(queryData, timeSyncF.Encode().Encode()...)
queryData = append(queryData, statusDataCf...) //数据帧包含结束帧 queryData = append(queryData, statusDataCf...) //数据帧包含结束帧
train.BtmState.BaliseTelegramForPcSimResend = fmt.Sprintf("%X", statusDataCf) train.BtmState.BaliseTelegramForPcSimResend = fmt.Sprintf("%X", statusDataCf)
train_pc_sim.Default().SendBaliseData(train_pc_sim.RECIVE_TRAIN_BTM_HAS_DATA, queryData) train_pc_sim.Default().SendBaliseData(train, train_pc_sim.RECIVE_TRAIN_BTM_HAS_DATA, queryData)
} else { } else {
slog.Error("列车pc仿真 BtmCanetClient应答帧、数据帧编码失败") slog.Error("列车pc仿真 BtmCanetClient应答帧、数据帧编码失败")
} }