rts-sim-testing-service/third_party/train_pc_sim/train_pc_sim.go
tiger_zhou cf3d38a2f5
Some checks failed
local-test分支打包构建docker并发布运行 / Docker-Build (push) Failing after 3m4s
列车驾驶ato牵引,制动初始版,添加列车紧急制动属性,添加ato开关门指示灯
2024-08-30 18:08:14 +08:00

554 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/sys_error"
"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"
"math"
"sync"
"time"
)
type TrainControlEvent struct {
Type byte
Data []byte
}
//var FireTrainControlEventType = ecs.NewEventType[TrainControlEvent]()
type TrainPcSim interface {
tpapi.ThirdPartyApiService
Start(pcSimManage TrainPcSimManage)
Stop()
// SendDriverActive Deprecated 发送驾驶端激活
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)
SendTrainControlMsg(train *state_proto.TrainState, baseMessage []message.TrainPcSimBaseMessage)
// CreateOrRemoveSpeedPLace 创建或删除速度位置信息
//CreateOrRemoveSpeedPLace(train *state_proto.TrainState)
// CreateOrRemoveTrain 创建或删除列车
CreateOrRemoveTrain(train *state_proto.TrainState, isCreate bool) error
// TrainPluseCount 计算列车脉冲
TrainPluseCount(sta *state_proto.TrainState, h1, h2, t1, t2 float32)
}
type TrainPcSimManage interface {
GetTrainPcSimConfig() []config.VehiclePCSimConfig
//GetConnTrain() *state_proto.TrainState
GetConnTrain2() []*state_proto.TrainState
//获取列车模拟量数据
ObtainTrainDigitalMockData(train *state_proto.TrainState) []message.TrainPcSimBaseMessage
// TrainPcSimDigitalOutInfoHandle 4.4.1. 车载输出数字量信息报文内容
TrainPcSimDigitalOutInfoHandle(train *state_proto.TrainState, data []byte)
// TrainPcSimDigitalReportHandle 4.4.2. 车载输出数字反馈量信息报文内容
TrainPcSimDigitalReportHandle(train *state_proto.TrainState, data []byte)
FindConnTrain(ct state_proto.TrainConnState_TrainConnType) *state_proto.TrainState
// TrainPcSimMockInfo 门模式
//TrainDoorModeHandle(state byte)
//处理列车pc仿真模拟量数据
TrainPcSimMockInfo(train *state_proto.TrainState, data []byte)
// TrainBtmQuery 处理列车btm查询
TrainBtmQuery2(train *state_proto.TrainState, data []byte)
}
type trainPcSimService struct {
state tpapi.ThirdPartyApiServiceState
newPcSimclientMap map[string]*TrainPcReciverData
cancleContextFun context.CancelFunc
context context.Context
trainPcSimManage TrainPcSimManage
configs []config.VehiclePCSimConfig
}
var (
initLock = &sync.Mutex{}
singleObj *trainPcSimService
)
func Default() TrainPcSim {
defer initLock.Unlock()
initLock.Lock()
if singleObj == nil {
singleObj = &trainPcSimService{}
}
return singleObj
}
const Name = "车载pc仿真"
func (d *trainPcSimService) Name() string {
return ""
}
func (d *trainPcSimService) State() tpapi.ThirdPartyApiServiceState {
return tpapi.ThirdPartyState_Closed
}
func (d *trainPcSimService) FindAppendApiService() []tpapi.ThirdPartyApiService {
return d.findAllThirdPartState()
}
func (d *trainPcSimService) TrueService() bool {
return false
}
func (d *trainPcSimService) ServiceDesc() string {
return Name
}
func FindTrainPcSimClientKey2(t *state_proto.TrainState) string {
return t.ConnState.TypeName
}
func (d *trainPcSimService) findTrainConn(sta *state_proto.TrainState) (*TrainPcReciverData, error) {
trainPcReciver := d.newPcSimclientMap[sta.ConnState.TypeName]
if trainPcReciver == nil {
return nil, fmt.Errorf("")
}
return trainPcReciver, nil
}
func (d *trainPcSimService) findAllThirdPartState() []tpapi.ThirdPartyApiService {
services := make([]tpapi.ThirdPartyApiService, 0)
for _, data := range d.newPcSimclientMap {
services = append(services, data)
}
return services
}
// 速度(单位mm/s)对应的脉冲数:速度*200/pi/840
// 里程单位mm对应的脉冲总里程*200/pi/840
func pluseCountSpeed(wheelDiameter int32, speedMeter float32) uint32 {
pluseCountData := speedMeter * 200 / math.Pi / float32(wheelDiameter)
return uint32(pluseCountData)
}
func (d *trainPcSimService) pluseSpeed(sta *state_proto.TrainState) (uint32, float32) {
defer initLock.Unlock()
initLock.Lock()
var sum float32 = 0
pcLen := len(sta.PluseCount.PulseCount3)
if pcLen == 0 {
return 0, 0
}
for _, f := range sta.PluseCount.PulseCount3 {
sum += f
}
d.TrainPluseCountReset(sta)
speed := sum / float32(pcLen)
return pluseCountSpeed(sta.WheelDiameter, speed*1000), speed
}
func (d *trainPcSimService) TrainPluseCount(sta *state_proto.TrainState, h1, h2, t1, t2 float32) {
defer initLock.Unlock()
initLock.Lock()
select {
case <-d.context.Done():
return
default:
}
if sd, err := d.findTrainConn(sta); err == nil {
sd.speedPlace.PulseCount1 += sta.DynamicState.Displacement
//sd.speedPlace.PulseCount1 += float32(uint32(h1 * 10))
sd.speedPlace.PulseCount2 = sd.speedPlace.PulseCount1
}
if sta.TrainRunUp {
if sta.TrainEndsA.SpeedSensorEnableA {
sta.PluseCount.PulseCount1 = pluseCountSpeed(sta.WheelDiameter, h1)
sta.PluseCount.PulseCount3 = append(sta.PluseCount.PulseCount3, h1)
}
if sta.TrainEndsA.SpeedSensorEnableB {
sta.PluseCount.PulseCount2 = pluseCountSpeed(sta.WheelDiameter, h2)
sta.PluseCount.PulseCount4 = append(sta.PluseCount.PulseCount3, h2)
}
} else {
if sta.TrainEndsB.SpeedSensorEnableA {
sta.PluseCount.PulseCount1 = pluseCountSpeed(sta.WheelDiameter, t1)
sta.PluseCount.PulseCount3 = append(sta.PluseCount.PulseCount3, t1)
}
if sta.TrainEndsB.SpeedSensorEnableB {
sta.PluseCount.PulseCount2 = pluseCountSpeed(sta.WheelDiameter, t2)
sta.PluseCount.PulseCount4 = append(sta.PluseCount.PulseCount3, t2)
}
}
}
func (d *trainPcSimService) TrainPluseCountReset(sta *state_proto.TrainState) {
sta.PluseCount.PulseCount1 = 0
sta.PluseCount.PulseCount2 = 0
sta.PluseCount.PulseCount3 = make([]float32, 0)
sta.PluseCount.PulseCount4 = make([]float32, 0)
}
func (d *trainPcSimService) newCloseAllConn() {
trains := d.trainPcSimManage.GetConnTrain2()
for _, train := range trains {
d.CreateOrRemoveTrain(train, false)
}
}
func (d *trainPcSimService) newCloseConn(clientKey string) {
rd := d.newPcSimclientMap[clientKey]
if rd != nil {
rd.tcpClient.Close()
rd.tcpClient = nil
rd.train = nil
rd.speedPlace = nil
//rd.trainInit = false
//d.cancleContextFun()
}
}
func (d *trainPcSimService) findConfig(configName string) (*config.VehiclePCSimConfig, error) {
for _, cfg := range d.configs {
if cfg.Open && cfg.ConfigName == configName {
return &cfg, nil
}
}
return nil, fmt.Errorf("未找到对应的车载pc连接配置")
}
func (d *trainPcSimService) initConn(clientKey string) error {
rd := d.newPcSimclientMap[clientKey]
if rd != nil && rd.tcpClient != nil && rd.tcpClient.IsConning() {
return nil
} else {
//rd.trainInit = false
rd.tcpClient = nil
}
cfg, cfgErr := d.findConfig(clientKey)
if cfgErr != nil {
errMsg := fmt.Sprintf("没找到对应的配置信息 key:%v", clientKey)
//slog.Error(errMsg, cfgErr.Error())
rd.updateState(tpapi.ThirdPartyState_Broken)
return sys_error.New(errMsg, cfgErr)
}
addr := fmt.Sprintf("%v:%v", cfg.PcSimIp, cfg.PcSimPort)
client2, err := tcp.StartTcpClient(addr, rd.receiverDataHandle, rd.readError)
if err != nil {
connErrMsg := fmt.Sprintf("车载pc连接失败 clientKey:%v", clientKey)
//slog.Error(connErrMsg, err.Error())
rd.updateState(tpapi.ThirdPartyState_Broken)
return sys_error.New(connErrMsg, err)
} else {
rd.tcpClient = client2
}
return nil
}
func (d *trainPcSimService) Start(pcSimManage TrainPcSimManage) {
configs := pcSimManage.GetTrainPcSimConfig()
d.newPcSimclientMap = make(map[string]*TrainPcReciverData)
if len(configs) <= 0 {
slog.Info("车载pc仿真配置未开启")
return
}
closedCount := 0
for _, c := range configs {
if !c.Open {
closedCount++
} else {
ck := c.ConfigName
pcReciver := &TrainPcReciverData{clientKey: ck, pcSimManage: pcSimManage}
pcReciver.updateState(tpapi.ThirdPartyState_Closed)
d.newPcSimclientMap[ck] = pcReciver
}
}
if closedCount == len(configs) {
slog.Error("车载pc仿真配置未开启")
return
}
d.configs = configs
ctx, ctxFun := context.WithCancel(context.Background())
d.cancleContextFun = ctxFun
d.context = ctx
d.trainPcSimManage = pcSimManage
go d.sendTrainLocationAndSpeedTask(ctx)
}
func (d *trainPcSimService) Stop() {
for _, data := range d.newPcSimclientMap {
data.updateState(tpapi.ThirdPartyState_Closed)
}
if d.cancleContextFun != nil {
d.cancleContextFun()
d.cancleContextFun = nil
}
d.newCloseAllConn()
}
func (d *trainPcSimService) CreateOrRemoveTrain(train *state_proto.TrainState, isCreate bool) error {
clientKey := FindTrainPcSimClientKey2(train)
err := d.initConn(clientKey)
if err != nil {
d.newCloseConn(clientKey)
return err
}
data := []byte{message.FLAG_CAMMAND_REMOVE_TRAIN}
if isCreate {
data[0] = message.FLAG_CAMMAND_CREATE_TRAIN
}
msg := &message.TrainPcSimBaseMessage{Data: data, Type: message.RECIVE_TRAIN_CREATE_REMOVE}
rd := d.newPcSimclientMap[clientKey]
if rd != nil {
initTrainErr := d.initTrain(rd, train, isCreate, msg)
if !isCreate {
d.newCloseConn(clientKey)
}
if initTrainErr != nil {
return initTrainErr
}
}
return nil
}
func (d *trainPcSimService) initTrain(rd *TrainPcReciverData, train *state_proto.TrainState, isCreate bool, trains *message.TrainPcSimBaseMessage) error {
msgs := make([]message.TrainPcSimBaseMessage, 0)
sendMsg := make([]byte, 0)
rd.speedPlace = &message.TrainSpeedPlaceReportMsg{}
train.PluseCount = &state_proto.SensorSpeedPulseCount{}
rd.train = train
tcc := train.Tcc
tcc.LineInitTimeStamp12 = 0
tcc.Line12ConnErr = false
if isCreate {
tmpMsgs := d.trainPcSimManage.ObtainTrainDigitalMockData(train)
msgs = append(msgs, tmpMsgs...)
msgs = append(msgs, message.TrainPcSimBaseMessage{Data: []byte{0x00}, Type: message.RECIVE_TRAIN_DOOR_MODE}) //门模式
msgs = append(msgs, message.TrainPcSimBaseMessage{Data: []byte{}, Type: message.RECIVE_TRAIN_BTN_CLEAR_ALL_PRE_DATA}) //清空应答器
} else {
train.VobcState.Tc1Active = false
train.VobcState.Tc2Active = false
for _, key := range tcc.DriverKey {
key.Val = false
}
msgs = append(msgs, message.TrainPcSimBaseMessage{Data: []byte{message.TRAIN_BRAKE_STATE, 0}, Type: message.SENDER_TRAIN_OUTR_INFO}) //驾驶室激活
msgs = append(msgs, message.TrainPcSimBaseMessage{Data: []byte{message.KEY_STATE, 0}, Type: message.SENDER_TRAIN_OUTR_INFO}) //驾驶室激活
msgs = append(msgs, message.TrainPcSimBaseMessage{Data: []byte{}, Type: message.SENDER_TRAIN_TC_NOT_ACTIVE}) //驾驶室激活
}
for _, msg := range msgs {
data := msg.Encode()
sendMsg = append(sendMsg, data...)
}
sendMsg = append(sendMsg, trains.Encode()...)
hexData := hex.EncodeToString(sendMsg)
slog.Info(fmt.Sprintf("发送列车初始化消息:%v", hexData))
rd.tcpClient.Send(sendMsg)
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 {
trainClient, trainDataErr := d.findTrainConn(train)
if trainDataErr != nil {
slog.Error(fmt.Sprintf("pc仿真速度位置未找到对应的列车 id:%v", train.Id))
continue
}
if trainClient.speedPlace == nil {
slog.Error(fmt.Sprintf("pc仿真速度位置脉冲对象为空 列车id:%v", train.Id))
continue
}
connState := tpapi.ThirdPartyState_Normal
if train.Tcc.Line12ConnErr {
connState = tpapi.ThirdPartyState_Broken
}
trainClient.updateState(connState)
s1, speed := d.pluseSpeed(train)
runDir := uint16(2)
if train.VobcState.DirectionForward {
runDir = 1
}
disPluse := pluseCountSpeed(train.WheelDiameter, trainClient.speedPlace.PulseCount1)
data := trainClient.speedPlace.Encode(runDir, s1, disPluse)
bm := &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_LOCATION_INFO, Data: data}
dataCode := bm.Encode()
slog.Info(fmt.Sprintf("发送列车速度位置,列车:%v,列车速度:%v,计数脉冲: %v,累计里程: %v ,发送数据:%v", train.Id, speed, s1, trainClient.speedPlace.PulseCount1, hex.EncodeToString(dataCode)))
err := trainClient.tcpClient.Send(dataCode)
if err != nil {
slog.Error(fmt.Sprintf("发送列车速度位置失败,列车:%v,发送数据:%v", train.Id, hex.EncodeToString(dataCode)))
}
}
}
time.Sleep(time.Millisecond * 80)
}
}
// SendDriverActive Deprecated 发送驾驶激活
func (d *trainPcSimService) SendDriverActive(train *state_proto.TrainState) {
trainClient, trainDataErr := d.findTrainConn(train)
if trainDataErr != nil {
slog.Error(fmt.Sprintf("发送驾驶激活未找到对应的列车连接列车id%v", train.Id))
return
}
vobc := train.VobcState
msg := &message.TrainPcSimBaseMessage{}
if train.TrainRunUp {
if vobc.Tc1Active {
msg.Type = message.SENDER_TRAIN_TC_ACTIVE
} else if vobc.Tc1Active == false {
msg.Type = message.SENDER_TRAIN_TC_NOT_ACTIVE
}
} else if !train.TrainRunUp {
if vobc.Tc2Active {
msg.Type = message.SENDER_TRAIN_TC_ACTIVE
} else if vobc.Tc2Active == false {
msg.Type = message.SENDER_TRAIN_TC_NOT_ACTIVE
}
}
msgs := make([]byte, 0)
if msg.Type == message.SENDER_TRAIN_TC_ACTIVE {
dd3 := message.TrainPcSimBaseMessage{Data: []byte{message.KEY_STATE, 1}, Type: message.SENDER_TRAIN_OUTR_INFO}
msgs = append(msgs, dd3.Encode()...)
} else {
dd3 := message.TrainPcSimBaseMessage{Data: []byte{message.KEY_STATE, 0}, Type: message.SENDER_TRAIN_OUTR_INFO}
msgs = append(msgs, dd3.Encode()...)
}
msgs = append(msgs, msg.Encode()...)
hexData := hex.EncodeToString(msgs)
//slog.Info(fmt.Sprintf("发送驾驶激活列车id:%v,数据:%v", train.Id, hexData))
err := trainClient.tcpClient.Send(msgs)
if err != nil {
slog.Error(fmt.Sprintf("发送驾驶激活失败列车id:%v,数据:%v,err:%v", train.Id, hexData, err.Error()))
}
}
func (d *trainPcSimService) SendHandleSwitch(oldTraction, oldBrakeForce int64, tractionState bool, train *state_proto.TrainState) {
trainClient, trainDataErr := d.findTrainConn(train)
if trainDataErr != nil {
slog.Error(fmt.Sprintf("发送列车牵引失败未找到对应的列车id:%v", train.Id))
return
}
tc := train.ConnState
if tc.Conn {
vobc := train.VobcState
msg := &message.TrainPcSimBaseMessage{}
newTraction := vobc.TractionForce
if tractionState {
if newTraction <= oldTraction && newTraction <= 0 {
//手柄取消前进
msg.Type = message.RECIVE_TRAIN_HAND_KEY_CANCLE_FORWARD
} else {
//手柄前进
msg.Type = message.SENDER_TRAIN_HAND_KEY_FORWARD
}
} else {
/*if newBrake >= newOldBrakeForce && newBrake == 0 {
//手柄取消后退
msg.Type = message.RECIVE_TRAIN_HAND_KEY_CACLE_BACKWARD
} else if newBrake < newOldBrakeForce {
//手柄后退
msg.Type = message.RECIVE_TRAIN_HAND_KEY_BACKWARD
} else {
//手柄后退
msg.Type = message.RECIVE_TRAIN_HAND_KEY_BACKWARD
}*/
msg.Type = message.RECIVE_TRAIN_HAND_KEY_BACKWARD
}
da := msg.Encode()
//slog.Info("发送列车手柄消息", "msg", hex.EncodeToString(da))
err := trainClient.tcpClient.Send(da)
//err := client.Send(da)
if err != nil {
slog.Error("发送列车手柄消息失败", "msg", hex.EncodeToString(da))
}
}
}
func (d *trainPcSimService) SendTrainDirection(train *state_proto.TrainState, trainForward, trainBackward bool) {
trainClient, trainDataErr := d.findTrainConn(train)
if trainDataErr != nil {
slog.Error(fmt.Sprintf("发送列车方向失败未找到列车连接trainId%s", train.Id))
return
}
baseMsgs := make([]*message.TrainPcSimBaseMessage, 0)
if !trainForward && !trainBackward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: message.RECIVE_TRAIN_HAND_KEY_CANCLE_FORWARD})
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: message.RECIVE_TRAIN_HAND_KEY_CACLE_BACKWARD})
} else if trainForward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: message.SENDER_TRAIN_HAND_KEY_FORWARD})
} else if trainBackward {
baseMsgs = append(baseMsgs, &message.TrainPcSimBaseMessage{Type: message.RECIVE_TRAIN_HAND_KEY_BACKWARD})
}
for _, msg := range baseMsgs {
da := msg.Encode()
//slog.Info(fmt.Sprintf("发送列车方向列车:%v ,数据:%v", train.Id, hex.EncodeToString(da)))
err := trainClient.tcpClient.Send(da)
if err != nil {
slog.Error(fmt.Sprintf("发送列车方向失败列车:%v ,数据:%v,err:%v", train.Id, hex.EncodeToString(da), err.Error()))
}
}
}
func (d *trainPcSimService) SendBaliseData(train *state_proto.TrainState, msgType byte, data []byte) {
trainClient, trainDataErr := d.findTrainConn(train)
if trainDataErr != nil {
slog.Error(fmt.Sprintf("发送列车PC仿真应答器信息失败未找到列车连接trainId%v", train.Id))
return
}
msg := &message.TrainPcSimBaseMessage{}
msg.Type = msgType
msg.Data = data
da := msg.Encode()
//slog.Info(fmt.Sprintf("发送列车PC仿真应答器信息,数据类型:0x%x源数据长度:%v,数据:%v", msgType, len(data), hex.EncodeToString(da)))
err := trainClient.tcpClient.Send(da)
if err != nil {
slog.Info(fmt.Sprintf("发送列车PC仿真应答器信息失败,数据:%v", hex.EncodeToString(da)))
}
}
func (d *trainPcSimService) SendTrainControlMsg(train *state_proto.TrainState, baseMessage []message.TrainPcSimBaseMessage) {
if len(baseMessage) <= 0 {
return
}
trainClient, trainDataErr := d.findTrainConn(train)
if trainDataErr != nil {
slog.Error(fmt.Sprintf("发送列车控制信息失败,无连接,列车Id:%v", train.Id))
return
}
for _, msg := range baseMessage {
dd := msg.Encode()
//slog.Info(fmt.Sprintf("发送列车控制信息:%x", dd))
d.sendData(trainClient.tcpClient, dd)
}
}
func (d *trainPcSimService) sendData(client *tcp.TcpClient, data []byte) {
err := client.Send(data)
if err != nil {
slog.Error(fmt.Sprintf("列车数字量信息发送失败,数据:%v", err.Error()))
}
}