rts-sim-testing-service/ts/simulation/wayside/memory/wayside_simulation_train_pc.go
2024-04-02 18:20:10 +08:00

405 lines
17 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 memory
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"joylink.club/bj-rtsts-server/config"
"joylink.club/bj-rtsts-server/const/balise_const"
"joylink.club/bj-rtsts-server/dto/request_proto"
"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/message"
train_pc_sim "joylink.club/bj-rtsts-server/third_party/train_pc_sim"
"log/slog"
"strings"
"time"
)
func (s *VerifySimulation) GetTrainPcSimConfig() config.VehiclePCSimConfig {
return s.runConfig.PcSimConfig
}
// 列车控制
func (s *VerifySimulation) ControlTrainUpdate(ct *request_proto.TrainControl) {
allTrainMap := &s.Memory.Status.TrainStateMap
data, ok := allTrainMap.Load(ct.TrainId)
if !ok {
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", ct.TrainId)))
}
sta := data.(*state_proto.TrainState)
var tce []train_pc_sim.TrainControlEvent = nil
if ct.ControlType == request_proto.TrainControl_EMERGENT_BUTTON {
trainControlEB(sta, ct.Button, ct.DeviceId)
} 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 {
oldTraction := sta.VobcState.TractionForce
oldBrakeForce := sta.VobcState.BrakeForce
tce = trainControlHandle(sta, ct.Handler, ct.DeviceId)
train_pc_sim.Default().SendHandleSwitch(oldTraction, oldBrakeForce, sta.ConnState, sta.VobcState)
}
if !sta.ConnState.Conn && sta.ConnState.ConnType != state_proto.TrainConnState_PC_SIM {
slog.Error("当前列车未连接车载pc仿真,", sta.Id)
return
}
train_pc_sim.Default().PublishTrainControlEvent(s.World, tce)
}
func trainControlEB(trainState *state_proto.TrainState, request *request_proto.TrainControl_EmergentButton, deviceId uint32) {
trainState.VobcState.EmergencyBrakingStatus = request.Active
if trainState.Tcc.Ebutton == nil {
trainState.Tcc.Ebutton = &state_proto.TrainControlState_EmergentButton{Id: deviceId, Passed: request.Active}
} else {
trainState.Tcc.Ebutton.Passed = request.Active
}
}
// 列车方向
func trainControlDirKey(trainState *state_proto.TrainState, request *request_proto.TrainControl_DirectionKeySwitch, deviceId uint32) {
trainState.VobcState.DirectionForward = false
trainState.VobcState.DirectionBackward = false
if request.Val == 1 {
trainState.VobcState.DirectionForward = true
} else if request.Val == 0 {
trainState.VobcState.DirectionBackward = false
}
if trainState.Tcc.DirKey == nil {
trainState.Tcc.DirKey = &state_proto.TrainControlState_DirectionKeySwitch{Id: deviceId, Val: request.Val}
} else {
trainState.Tcc.DirKey.Val = request.Val
}
}
// 列车驾驶端激活
func trainControlDriverKey(trainState *state_proto.TrainState, request *request_proto.TrainControl_DriverKeySwitch, deviceId uint32) []train_pc_sim.TrainControlEvent {
if request.Dt == request_proto.DriverType_ONE_END {
trainState.VobcState.Tc1Active = request.Val
} else if request.Dt == request_proto.DriverType_TWO_END {
trainState.VobcState.Tc2Active = request.Val
}
var addNew = true
for _, k := range trainState.Tcc.DriverKey {
if k.Id == deviceId {
k.Id = deviceId
k.Val = request.Val
addNew = false
break
}
}
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)}}
}
// 列车牵引控制
func trainControlHandle(trainState *state_proto.TrainState, request *request_proto.TrainControl_PushHandler, deviceId uint32) []train_pc_sim.TrainControlEvent {
trainState.VobcState.TractionStatus = false
trainState.VobcState.TractionForce = 0
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})
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})
} 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})
} else {
tce = append(tce, train_pc_sim.TrainControlEvent{Command: train_pc_sim.HANDLE_TO_ZERO, Status: 1})
}
if trainState.Tcc.PushHandler == nil {
trainState.Tcc.PushHandler = &state_proto.TrainControlState_PushHandler{Id: deviceId, Val: request.Val}
} else {
trainState.Tcc.PushHandler.Val = request.Val
}
return tce
}
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 {
train := v.(*state_proto.TrainState)
connState := train.ConnState
if connState.ConnType == ct {
findTrain = train
return false
}
return true
})
return findTrain
}
// 4.4.1. 车载输出数字量信息报文内容
func (s *VerifySimulation) TrainPcSimDigitalOutInfoHandle(data []byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM)
if train == nil {
slog.Error("车载输出数字量未找到连接车载pc仿真的列车")
return
}
if !train.ConnState.Conn {
slog.Error("车载输出数字量,,列车未连接车载pc仿真")
return
}
buf := bytes.NewBuffer(data)
vobc := train.VobcState
cutTraction, _ := buf.ReadByte() //切牵引
trainDoorOutLed, _ := buf.ReadByte() //车门外指示灯
stopBrakeAppend, _ := buf.ReadByte() //停放制动施加
emergentBrake, _ := buf.ReadByte() //紧急制动
leftOpenDoor, _ := buf.ReadByte() //开左门允许
rightOpenDoor, _ := buf.ReadByte() //开右门允许
closeRightDoor, _ := buf.ReadByte() //关右门
doorAlwaysClosed, _ := buf.ReadByte() //车门保持关闭
localAtpControl, _ := buf.ReadByte() //本端ATP控车
atoMode, _ := buf.ReadByte() //ATO模式
atoTractionCommandOut, _ := buf.ReadByte() //ATO牵引命令输出
atoTractionCommand1, _ := buf.ReadByte() //ATO牵引指令1
atoTractionCommand2, _ := buf.ReadByte() //ATO牵引指令2
atoTractionCommand3, _ := buf.ReadByte() //ATO牵引指令3
atoBrakeCommand, _ := buf.ReadByte() //ATO制动命令输出
skipCommand, _ := buf.ReadByte() //跳跃指令
direction1, _ := buf.ReadByte() //列车方向1
direction2, _ := buf.ReadByte() //列车方向2
atoLazyCommandOut, _ := buf.ReadByte() //ATO惰行命令输出
sleepCommand, _ := buf.ReadByte() //休眠指令
wakeUpCommand, _ := buf.ReadByte() //唤醒指令
toPullTrainLed, _ := buf.ReadByte() //ATO发车指示灯
arLightCommand, _ := buf.ReadByte() //AR灯命令
atoAlwaysBrake, _ := buf.ReadByte() //ATO保持制动
atoOpenLeftDoor, _ := buf.ReadByte() //ATO开左门
atoOpenRightDoor, _ := buf.ReadByte() //ATO开右门
atoCloseLeftDoor, _ := buf.ReadByte() //ATO关左门
ariverActive, _ := buf.ReadByte() //驾驶室激活
noSpeedSigle, _ := buf.ReadByte() //零速信号
famMode, _ := buf.ReadByte() //FAM模式
camMode, _ := buf.ReadByte() //CAM模式
trainStartedLed, _ := buf.ReadByte() //列车启动指示灯
mostUseBrake, _ := buf.ReadByte() //常用制动
splittingOut, _ := buf.ReadByte() //过分相输出
modeRelay, _ := buf.ReadByte() //模式继电器
tractionEffective, _ := buf.ReadByte() //牵引有效
brakeEffective, _ := buf.ReadByte() //制动有效
lifeDoorUsed, _ := buf.ReadByte() //逃生门使能
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.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.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) //? //停放制动缓解
}
// 4.4.2. 车载输出数字反馈量信息报文内容
func (s *VerifySimulation) TrainPcSimDigitalReportHandle(data []byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM)
if train == nil {
slog.Error("车载输出数字反馈量信息,未找到连接车载pc仿真的列车")
return
}
if !train.ConnState.Conn {
slog.Error("车载输出数字反馈量信息,列车未连接车载pc仿真")
return
}
vobc := train.VobcState
buf := bytes.NewBuffer(data)
localEndAct, _ := buf.ReadByte()
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) //方向手柄退位
}
// 创建/删除列车
func (s *VerifySimulation) TrainPcSimConnOrRemoveHandle(state byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM)
if train == nil {
slog.Error("创建删除列车未找到连接车载pc仿真的列车")
return
}
connState := train.ConnState
if state == 0x01 {
connState.Conn = true
} else {
connState.Conn = false
}
}
// 门模式
func (s *VerifySimulation) TrainDoorModeHandle(state byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM)
if train == nil {
slog.Error("车载pc仿真门模式未找到连接车载pc仿真的列车")
return
}
if !train.ConnState.Conn {
slog.Error("车载pc仿真门模式,列车未连接车载pc仿真")
return
}
switch state {
case 0x00:
//0x00表示自开自关AA
train.VobcState.DoorModeAA = true
case 0x01:
//0x01表示自开人关AM
train.VobcState.DoorModeAM = true
case 0x02:
//0x02表示人开人关MM
train.VobcState.DoorModeMM = true
}
}
// 4.4.3. 车载输出模拟量信息报文内容(0x03)
func (s *VerifySimulation) TrainPcSimMockInfo(data []byte) {
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM)
if train == nil {
slog.Error("车载输出模拟量,未找到连接车载pc仿真的列车")
return
}
if !train.ConnState.Conn {
slog.Error("车载输出模拟量,列车未连接车载pc仿真")
return
}
mockData := binary.BigEndian.Uint16(data)
train.VobcState.MockInfo = uint32(mockData)
}
// 4.4.4. 车载输出BTM查询同步帧报文内容0x04
func (s *VerifySimulation) TrainBtmQuery(data []byte) {
if len(data) < 12 {
slog.Error("列车btm查询报文长度错误:", len(data))
return
}
train := s.findConnTrain(state_proto.TrainConnState_PC_SIM)
if train == nil {
slog.Error("车载输出btm查询,未找到连接车载pc仿真的列车")
return
}
if !train.ConnState.Conn {
slog.Error("车载输出btm查询,列车未连接车载pc仿真")
return
}
trainAtm := message.NewCanetFrame(data, true)
atpReq := &message.AtpRequestFrame{}
if !atpReq.Decode(trainAtm) {
slog.Warn("列车pc驾驶模拟-CanetFrame解码成AtpRequestFrame失败", "CanetFrame", trainAtm.String())
return
}
cl := clock(atpReq)
btmRepFrame := createBtmStatus(trainAtm.CanId.ID4, train.BtmState, atpReq, cl)
if atpReq.ResendRequest == 2 {
//重新发送
if len(train.BtmState.BaliseTelegramForPcSimResend) > 0 {
dd, _ := hex.DecodeString(train.BtmState.BaliseTelegramForPcSimResend)
train_pc_sim.Default().SendBaliseData(train_pc_sim.RECIVE_TRAIN_BTM_HAS_DATA, dd)
}
} else {
timeSyncF := message.NewBtmTimeSyncCheckFrame(trainAtm.CanId.ID4, true)
timeSyncF.T2 = cl.BtmTk
timeSyncF.T3 = cl.TkNow()
telCount := strings.Count(train.BtmState.Telegram, "00")
if telCount >= balise_const.UserTelegramByteLen {
//无数据
queryData := make([]byte, 0)
queryData = append(queryData, btmRepFrame.Encode().Encode()...)
queryData = append(queryData, timeSyncF.Encode().Encode()...)
train_pc_sim.Default().SendBaliseData(train_pc_sim.RECIVE_TRAIN_BTM_NOT_DATA, queryData)
} else {
//有数据
aliseData, _ := hex.DecodeString(train.BtmState.Telegram)
statusDataCf, statusDataCfOk := message.CreateBtmRspFramesData(btmRepFrame, aliseData, false, cl.TkNow(), cl.TkNow(), cl.TkNow(), true)
if statusDataCfOk {
queryData := make([]byte, 0)
queryData = append(queryData, btmRepFrame.Encode().Encode()...)
queryData = append(queryData, timeSyncF.Encode().Encode()...)
queryData = append(queryData, statusDataCf...) //数据帧包含结束帧
train.BtmState.BaliseTelegramForPcSimResend = fmt.Sprintf("%X", statusDataCf)
train_pc_sim.Default().SendBaliseData(train_pc_sim.RECIVE_TRAIN_BTM_HAS_DATA, queryData)
} else {
slog.Error("列车pc仿真 BtmCanetClient应答帧、数据帧编码失败")
}
}
}
}
func createBtmStatus(canIdSn byte, btmState *state_proto.BTMState, atpReq *message.AtpRequestFrame, cl can_btm.BtmClock) *message.BtmStatusRspFrame {
statusF := message.NewBtmStatusRspFrame(canIdSn, true)
//btmStatus := aa(train, atpReq)
statusF.PowerAmplifierOn = true
statusF.PowerAmplifierFailure = false
statusF.AtpReqCrcCheckWrong = !atpReq.Crc16CheckOk
statusF.AntennaFault = false
statusF.BaliseCounter = byte(btmState.BaliseCount)
statusF.MessageCounter = byte(btmState.MessageCounter)
statusF.TkTimeA = cl.TkNow()
statusF.DetailedCode = 0
if btmState.AboveBalise {
statusF.DetailedCode = 0x07
}
statusF.Dsn = byte(btmState.DataSerialNumber)
return statusF
}
func clock(atpReq *message.AtpRequestFrame) can_btm.BtmClock {
now := time.Now()
return can_btm.BtmClock{BtmTk: atpReq.Time, SysTk: now}
}