rts-sim-testing-service/third_party/train_pc_sim/train_pc_sim.go

445 lines
13 KiB
Go

package train_pc_sim
import (
"context"
"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 uint16, 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)
}
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
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) closeAllConn() {
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() {
for {
select {
case <-ctx.Done():
d.closeAllConn()
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)
continue
}
client := d.pcSimClientMap[clientKey]
if !client.IsConning() {
client.Close()
d.initConn(clientKey)
}
}
}
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 {
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.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()
}
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)
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发送列车速度位置
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)
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]
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
}
}
client.Send(msg.Encode())
}
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)
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
}
}
client.Send(msg.Encode())
}
}
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)
client := d.pcSimClientMap[clientKey]
for _, msg := range baseMsgs {
client.Send(msg.Encode())
}
}
func (d *trainPcSimService) SendBaliseData(train *state_proto.TrainState, msgType uint16, data []byte) {
msg := &message.TrainPcSimBaseMessage{}
msg.Type = msgType
msg.Data = data
clientKey := FindTrainPcSimClientKey(train)
client := d.pcSimClientMap[clientKey]
//fmt.Println(fmt.Sprintf("%X", msg.Encode()))
client.Send(msg.Encode())
}
/*
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)
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
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])
}
}