rts-sim-testing-service/third_party/train_pc_sim/train_pc_sim.go
tiger_zhou cbd2c4a0ab
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m45s
日志调整及现场测试
2024-06-14 09:21:51 +08:00

582 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package train_pc_sim
import (
"context"
"encoding/hex"
"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/bj-rtsts-server/third_party/tpapi"
"log/slog"
"sync"
"time"
)
type TrainControlEvent struct {
Command byte
Status byte
}
//var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]()
type TrainPcSim interface {
tpapi.ThirdPartyApiService
Start(pcSimManage TrainPcSimManage)
Stop()
// SendDriverActive 发送驾驶端激活
SendDriverActive(train *state_proto.TrainState)
// SendHandleSwitch 发送牵引制动手柄
SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState)
// SendTrainDirection 列车运行方向
//因文档说明不清楚,在调用的时候目前是注释状态,现场调试可能会用到
SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool)
//发送应答器信息数据
SendBaliseData(train *state_proto.TrainState, msgType byte, data []byte)
//发布列车控制的相关事件
PublishTrainControlEvent(train *state_proto.TrainState, events []TrainControlEvent)
// CreateOrRemoveSpeedPLace 创建或删除速度位置信息
CreateOrRemoveSpeedPLace(train *state_proto.TrainState)
// CreateOrRemoveTrain 创建或删除列车
CreateOrRemoveTrain(train *state_proto.TrainState, msgType byte, data []byte) error
}
type TrainPcSimManage interface {
GetTrainPcSimConfig() []config.VehiclePCSimConfig
//GetConnTrain() *state_proto.TrainState
GetConnTrain2() []*state_proto.TrainState
// TrainPcSimDigitalOutInfoHandle 4.4.1. 车载输出数字量信息报文内容
TrainPcSimDigitalOutInfoHandle(connType state_proto.TrainConnState_TrainConnType, data []byte)
// TrainPcSimDigitalReportHandle 4.4.2. 车载输出数字反馈量信息报文内容
TrainPcSimDigitalReportHandle(connType state_proto.TrainConnState_TrainConnType, data []byte)
// TrainPcSimMockInfo 门模式
//TrainDoorModeHandle(state byte)
//处理列车pc仿真模拟量数据
TrainPcSimMockInfo(connType state_proto.TrainConnState_TrainConnType, data []byte)
// TrainBtmQuery 处理列车btm查询
TrainBtmQuery(connType state_proto.TrainConnState_TrainConnType, data []byte)
}
type trainPcReciverData struct {
clientKey string
tcpClient *tcp.TcpClient
pcSimManage TrainPcSimManage
}
func (rd *trainPcReciverData) receiverDataHandle(n int, data []byte) {
connType := state_proto.TrainConnState_PC_SIM_A
if rd.clientKey == "B" {
connType = state_proto.TrainConnState_PC_SIM_B
}
baseMsg := &message.TrainPcSimBaseMessage{}
err := baseMsg.Decode(data)
if err != nil {
slog.Error("车载pc仿真接受数据解析失败 ")
return
}
slog.Info(fmt.Sprintf("pc仿真接收数据%v,类型:%v", hex.EncodeToString(data), baseMsg.Type))
switch baseMsg.Type {
//case RECIVE_TRAIN_CREATE_REMOVE:
// pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0])
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR:
rd.pcSimManage.TrainPcSimDigitalOutInfoHandle(connType, baseMsg.Data)
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK:
rd.pcSimManage.TrainPcSimDigitalReportHandle(connType, baseMsg.Data)
case RECIVE_TRAIN_QUERY_STATUS:
rd.pcSimManage.TrainBtmQuery(connType, baseMsg.Data)
case RECIVE_TRAIN_MOCK_DATA:
rd.pcSimManage.TrainPcSimMockInfo(connType, baseMsg.Data)
//case RECIVE_TRAIN_DOOR_MODE:
// pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0])
}
}
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 {
return Name
}
func (d *trainPcSimService) State() tpapi.ThirdPartyApiServiceState {
return d.state
}
func (d *trainPcSimService) updateState(state tpapi.ThirdPartyApiServiceState) {
d.state = state
}
var (
initLock = &sync.Mutex{}
singleObj *trainPcSimService
)
func Default() TrainPcSim {
defer initLock.Unlock()
initLock.Lock()
if singleObj == nil {
singleObj = &trainPcSimService{}
}
return singleObj
}
type trainPcSimService struct {
state tpapi.ThirdPartyApiServiceState
//pcSimClient *tcp.TcpClient
//pcSimClientMap map[string]*tcp.TcpClient
newPcSimclientMap map[string]*trainPcReciverData
cancleContext context.CancelFunc
trainPcSimManage TrainPcSimManage
speedPlace *message.TrainSpeedPlaceReportMsg
configs []config.VehiclePCSimConfig
}
// 接受来自pc仿真的消息
func (d *trainPcSimService) readError(err error) {
slog.Error("连接车载pc仿真tcp服务断开", err)
d.updateState(tpapi.ThirdPartyState_Broken)
}
func (d *trainPcSimService) newCloseAllConn() {
for _, rd := range d.newPcSimclientMap {
if rd != nil {
rd.tcpClient.Close()
}
}
}
/*
func (d *trainPcSimService) closeAllConn() {
for key, client := range d.pcSimClientMap {
if client != nil {
client.Close()
}
delete(d.pcSimClientMap, key)
}
}
*/
func (d *trainPcSimService) newCloseConn(clientKey string) {
rd := d.newPcSimclientMap[clientKey]
if rd != nil {
rd.tcpClient.Close()
}
}
/*
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) {
go func() {
for {
select {
case <-ctx.Done():
//d.closeAllConn()
d.newCloseAllConn()
return
default:
}
trains := d.trainPcSimManage.GetConnTrain2()
if len(trains) > 0 {
for _, t := range trains {
clientKey := FindTrainPcSimClientKey(t)
if clientKey == "" {
slog.Error("未找到对应的pc仿真连接,trainId:", t.Id, "删除对应客户端")
//d.closeConn(clientKey)
d.newCloseConn(clientKey)
continue
}
//client := d.pcSimClientMap[clientKey]
rd := d.newPcSimclientMap[clientKey]
if rd == nil {
d.newPcSimclientMap[clientKey] = &trainPcReciverData{pcSimManage: d.trainPcSimManage, clientKey: clientKey, tcpClient: &tcp.TcpClient{}}
}
if !rd.tcpClient.IsConning() {
//client.Close()
d.newCloseConn(clientKey)
d.initConn(clientKey)
}
}
}
time.Sleep(time.Second)
}
}()
}
func (d *trainPcSimService) initConn(clientKey string) {
rd := d.newPcSimclientMap[clientKey]
if rd == nil {
rd = &trainPcReciverData{pcSimManage: d.trainPcSimManage, clientKey: clientKey, tcpClient: &tcp.TcpClient{}}
d.newPcSimclientMap[clientKey] = rd
}
cfg, _ := d.findConfig(clientKey)
addr := fmt.Sprintf("%v:%v", cfg.PcSimIp, cfg.PcSimPort)
client2, err := tcp.StartTcpClient(addr, rd.receiverDataHandle, d.readError)
if err != nil {
slog.Error("车载pc连接失败 clientKey:", clientKey, "error:", err.Error())
d.updateState(tpapi.ThirdPartyState_Broken)
} else {
rd.tcpClient = client2
}
/*if d.pcSimClientMap[clientKey] != nil {
return
}
cfg, _ := d.findConfig(clientKey)
addr := fmt.Sprintf("%v:%v", cfg.PcSimIp, cfg.PcSimPort)
properties := map[string]interface{}{
CLIENT_KEY: clientKey,
}
client2, err := tcp.StartTcpClient(addr, properties, d.reivceData, d.readError)
if err != nil {
slog.Error("车载pc连接失败 clientKey:", clientKey, "error:", err.Error())
d.updateState(tpapi.ThirdPartyState_Broken)
} else {
d.pcSimClientMap[clientKey] = client2
}*/
}
func (d *trainPcSimService) Start(pcSimManage TrainPcSimManage) {
configs := pcSimManage.GetTrainPcSimConfig()
d.newPcSimclientMap = make(map[string]*trainPcReciverData)
//d.pcSimClientMap = map[string]*tcp.TcpClient{}
if len(configs) <= 0 {
slog.Info("车载pc仿真配置未开启")
return
}
allClosed := true
for _, c := range configs {
if !c.Open {
allClosed = false
}
}
if !allClosed {
slog.Info("车载pc仿真配置未开启")
return
}
d.configs = configs
ctx, ctxFun := context.WithCancel(context.Background())
d.cancleContext = ctxFun
d.trainPcSimManage = pcSimManage
d.connTrainPcSim(ctx)
//FireTrainControlEventType.Subscribe(wd, d.trainControlEventHandle)
d.updateState(tpapi.ThirdPartyState_Normal)
go d.sendTrainLocationAndSpeedTask(ctx)
}
func (d *trainPcSimService) Stop() {
d.updateState(tpapi.ThirdPartyState_Closed)
if d.cancleContext != nil {
d.cancleContext()
d.cancleContext = nil
}
//d.closeAllConn()
d.newCloseAllConn()
}
func (d *trainPcSimService) CreateOrRemoveSpeedPLace(train *state_proto.TrainState) {
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{}
d.speedPlace = &message.TrainSpeedPlaceReportMsg{TrainId: train.Id}
} else {
train.PluseCount = nil
d.speedPlace = nil
}
}
func (d *trainPcSimService) CreateOrRemoveTrain(train *state_proto.TrainState, msgType byte, data []byte) error {
clientKey := FindTrainPcSimClientKey(train)
log := "删除列车"
if msgType == RECIVE_TRAIN_CREATE_REMOVE && data[0] == 0x01 {
log = "创建列车"
d.initConn(clientKey)
}
msg := &message.TrainPcSimBaseMessage{Data: data, Type: msgType}
rd := d.newPcSimclientMap[clientKey]
if rd != nil {
sd := msg.Encode()
slog.Info(fmt.Sprintf("%v-列车号:%v,发送数据:%v", log, train.Id, hex.EncodeToString(sd)))
err := rd.tcpClient.Send(sd)
if err != nil {
return err
}
if data[0] != 0x01 {
d.newCloseConn(clientKey)
}
}
/*client := d.pcSimClientMap[clientKey]
err := client.Send(msg.Encode())
if data[0] != 0x01 {
d.closeConn(clientKey)
}*/
return nil
}
// 依据文档80ms发送列车速度位置
func (d *trainPcSimService) sendTrainLocationAndSpeedTask(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
}
trains := d.trainPcSimManage.GetConnTrain2()
for _, train := range trains {
if train.ConnState.Conn && train.PluseCount != nil {
clientKey := FindTrainPcSimClientKey(train)
rd := d.newPcSimclientMap[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
rd.tcpClient.Send(bm.Encode())
/*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())*/
}
}
time.Sleep(time.Millisecond * 80)
}
}
// 发送驾驶激活
func (d *trainPcSimService) SendDriverActive(train *state_proto.TrainState) {
vobc := train.VobcState
clientKey := FindTrainPcSimClientKey(train)
//client := d.pcSimClientMap[clientKey]
rd := d.newPcSimclientMap[clientKey]
defulatBuf := make([]byte, 0)
msg := &message.TrainPcSimBaseMessage{Data: defulatBuf}
if train.TrainRunUp {
if vobc.Tc1Active {
msg.Type = SENDER_TRAIN_TC_ACTIVE
} 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
}
}
//:"创建列车-列车号:1,发送数据:eb0050010156e4"}
//{,"msg":"发送驾驶激活列车","1":"数据","!BADKEY":"eb0004002437"}
da := msg.Encode()
slog.Info("发送驾驶激活列车", train.Id, "数据", hex.EncodeToString(da))
err := rd.tcpClient.Send(da)
//err := client.Send(da)
if err != nil {
slog.Error("发送驾驶激活列车", train.Id, "数据", hex.EncodeToString(da), err)
}
}
func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState) {
tc := train.ConnState
if tc.Conn {
vobc := train.VobcState
clientKey := FindTrainPcSimClientKey(train)
rd := d.newPcSimclientMap[clientKey]
//client := d.pcSimClientMap[clientKey]
msg := &message.TrainPcSimBaseMessage{}
newTraction := vobc.TractionForce
newBrake := -vobc.BrakeForce
newOldBrakeForce := -oldBrakeForce
if tractionState {
if newTraction <= oldTraction && newTraction == 0 {
//手柄取消前进
msg.Type = RECIVE_TRAIN_HAND_KEY_CANCLE_FORWARD
} else if newTraction > oldTraction {
//手柄前进
msg.Type = SENDER_TRAIN_HAND_KEY_FORWARD
} else {
//手柄前进
msg.Type = SENDER_TRAIN_HAND_KEY_FORWARD
}
} else {
if newBrake >= newOldBrakeForce && newBrake == 0 {
//手柄取消后退
msg.Type = RECIVE_TRAIN_HAND_KEY_CACLE_BACKWARD
} else if newBrake < newOldBrakeForce {
//手柄后退
msg.Type = RECIVE_TRAIN_HAND_KEY_BACKWARD
} else {
//手柄后退
msg.Type = RECIVE_TRAIN_HAND_KEY_BACKWARD
}
}
da := msg.Encode()
slog.Info("发送列车手柄消息", "clientKey", clientKey, "msg", hex.EncodeToString(da))
err := rd.tcpClient.Send(da)
//err := client.Send(da)
if err != nil {
slog.Error("发送列车手柄消息失败", "clientKey", clientKey, "msg", hex.EncodeToString(da))
}
}
}
func (d *trainPcSimService) SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool) {
baseMsgs := make([]*message.TrainPcSimBaseMessage, 0)
if !trainForward && !trainBackward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_CANCLE_FORWARD})
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_CACLE_BACKWARD})
} else if trainForward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: SENDER_TRAIN_HAND_KEY_FORWARD})
} else if trainBackward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: RECIVE_TRAIN_HAND_KEY_BACKWARD})
}
clientKey := FindTrainPcSimClientKey(train)
rd := d.newPcSimclientMap[clientKey]
//client := d.pcSimClientMap[clientKey]
for _, msg := range baseMsgs {
da := msg.Encode()
slog.Info("发送列车方向列车", train.Id, "数据", hex.EncodeToString(da))
err := rd.tcpClient.Send(da)
//err := client.Send(da)
if err != nil {
slog.Error("发送列车方向失败列车", train.Id, "数据", hex.EncodeToString(da))
}
}
}
func (d *trainPcSimService) SendBaliseData(train *state_proto.TrainState, msgType byte, data []byte) {
msg := &message.TrainPcSimBaseMessage{}
msg.Type = msgType
msg.Data = data
clientKey := FindTrainPcSimClientKey(train)
rd := d.newPcSimclientMap[clientKey]
//client := d.pcSimClientMap[clientKey]
da := msg.Encode()
slog.Info("发送列车PC仿真应答器信息,数据", hex.EncodeToString(da))
err := rd.tcpClient.Send(da)
if err != nil {
slog.Info("发送列车PC仿真应答器信息失败,数据", hex.EncodeToString(da))
}
}
/*
func (d *trainPcSimService) trainControlEventHandle( event TrainControlEvent) {
msg := &message.TrainPcSimBaseMessage{}
msg.Type = SENDER_TRAIN_OUTR_INFO
data := []byte{event.Command, event.Status}
msg.Data = data
d.pcSimClient.Send(msg.Encode())
}
*/
func (d *trainPcSimService) PublishTrainControlEvent(train *state_proto.TrainState, events []TrainControlEvent) {
if len(events) <= 0 {
slog.Warn("发布事件数量为空")
return
}
clientKey := FindTrainPcSimClientKey(train)
rd := d.newPcSimclientMap[clientKey]
//client := d.pcSimClientMap[clientKey]
for _, event := range events {
msg := &message.TrainPcSimBaseMessage{}
msg.Type = SENDER_TRAIN_OUTR_INFO
data := []byte{event.Command, event.Status}
msg.Data = data
code := msg.Encode()
hexCode := hex.EncodeToString(code)
slog.Info(fmt.Sprintf("输出列车控制输出量,命令码位:%v 对应状态:%v,发送数据:%v", event.Command, event.Status, hexCode))
err := rd.tcpClient.Send(code)
if err != nil {
slog.Error(fmt.Sprintf("输出列车控制输出量发送失败,命令码位:%v 对应状态:%v,发送数据:%v", event.Command, event.Status, hexCode))
}
//client.Send(msg.Encode())
//FireTrainControlEventType.Publish(world, &event)
}
}
// 接受来自pc仿真的消息
/*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{}
err := baseMsg.Decode(data)
if err != nil {
slog.Error("车载pc仿真接受数据解析失败 ")
return
}
switch baseMsg.Type {
//case RECIVE_TRAIN_CREATE_REMOVE:
// pc.trainPcSimManage.TrainPcSimConnOrRemoveHandle(baseMsg.Data[0])
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR:
d.trainPcSimManage.TrainPcSimDigitalOutInfoHandle(connType, baseMsg.Data)
case RECIVE_TRAIN_INTERFACE_CABINET_OUTR_BACK:
d.trainPcSimManage.TrainPcSimDigitalReportHandle(connType, baseMsg.Data)
case RECIVE_TRAIN_QUERY_STATUS:
d.trainPcSimManage.TrainBtmQuery(connType, baseMsg.Data)
case RECIVE_TRAIN_MOCK_DATA:
d.trainPcSimManage.TrainPcSimMockInfo(connType, baseMsg.Data)
//case RECIVE_TRAIN_DOOR_MODE:
// pc.trainPcSimManage.TrainDoorModeHandle(baseMsg.Data[0])
}
}*/