[新增]11号线驾驶台与工装通信服务

This commit is contained in:
thesai 2024-11-05 13:59:46 +08:00
parent cc71c128cf
commit 64a5989b71
8 changed files with 1028 additions and 19 deletions

View File

@ -15,6 +15,7 @@ import (
"time"
)
// AccVobc 加速度计通信服务接口
type AccVobc interface {
Start(accManager AccVobcManager)
Stop()

View File

@ -18,7 +18,7 @@ const (
PointB
)
// 电机转速UDP
// ElectricMachinery 速传通信服务接口
type ElectricMachinery interface {
Start(manager ElectricMachineryMessageManager) // 启动电机转速UDP消息处理
Stop() // 停止电机转速消息处理

View File

@ -0,0 +1,254 @@
package beijing11
import (
"encoding/binary"
"errors"
"fmt"
"github.com/snksoft/crc"
)
// HC初始化命令帧
type hcInitFrame struct {
timestamp hcTimestampPackage
modeInfo modeInfoPackage
timeSyncInfo timeSyncInfoPackage
reqScSelfCheckInfo reqScSelfCheckInfoPackage
}
func (h *hcInitFrame) encode() []byte {
data := make([]byte, 0, 21)
data = append(data, FrameHead)
data = append(data, 0, 0) //帧长度占位
data = append(data, h.timestamp.encode()...)
data = append(data, h.modeInfo.encode()...)
data = append(data, h.timeSyncInfo.encode()...)
data = append(data, h.reqScSelfCheckInfo.encode()...)
data = escape(data)
binary.BigEndian.PutUint16(data[1:], uint16(len(data)-3)) //填充帧长度
data = binary.BigEndian.AppendUint32(data, crc32(data))
data = append(data, FrameTail)
return data
}
// SC初始化确认帧
type scInitFrame struct {
timestamp scTimestampPackage
modeConfirm scModeConfirmPackage
timeSyncConfirm scTimeSyncConfirmPackage
scSelfCheckConfirm scSelfCheckConfirmPackage
}
func (s *scInitFrame) decode(frame []byte) error {
if frame[0] != FrameHead {
return errors.New(fmt.Sprintf("SC初始化确认帧解析失败帧头不对%x", frame[0]))
}
if uint16(len(frame)-8) != binary.BigEndian.Uint16(frame[1:3]) {
return errors.New(fmt.Sprintf("SC初始化确认帧解析失败数据长度不对期望[%x] 实际[%x]", binary.BigEndian.Uint16(frame[1:3]), len(frame)))
}
if binary.BigEndian.Uint32(frame[len(frame)-5:len(frame)-1]) != crc32(frame[:len(frame)-5]) {
return errors.New(fmt.Sprintf("SC初始化确认帧解析失败CRC校验不通过期望[%x] 实际[%x]",
crc32(frame[:len(frame)-5]), binary.BigEndian.Uint32(frame[len(frame)-5:len(frame)-1])))
}
start := 3
err := s.timestamp.decode(frame[start : start+5])
if err != nil {
return err
}
start += 5
err = s.modeConfirm.decode(frame[start : start+2])
if err != nil {
return err
}
start += 2
err = s.timeSyncConfirm.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.scSelfCheckConfirm.decode(frame[start : start+2])
if err != nil {
return err
}
return nil
}
// HC控制帧
type hcControlFrame struct {
timestamp hcTimestampPackage
io1 []byte
io2 []byte
io3 []byte
io4 []byte
io5 []byte
pwm1 hcPwmControlPackage
pwm2 hcPwmControlPackage
pwm3 hcPwmControlPackage
pwm4 hcPwmControlPackage
pwm5 hcPwmControlPackage
pwm6 hcPwmControlPackage
analog1 uint16
analog2 uint16
analog3 uint16
analog4 uint16
analog5 uint16
analog6 uint16
cd hcCDControlPackage
output422 hc422ControlPackage
output485 hc485ControlPackage
}
func (h *hcControlFrame) encode() []byte {
data := make([]byte, 0, 600)
data = append(data, FrameHead)
data = append(data, 0, 0) //帧长度占位
data = append(data, h.timestamp.encode()...)
data = append(data, HcIo1)
data = append(data, h.io1...)
data = append(data, HcIo2)
data = append(data, h.io2...)
data = append(data, HcIo3)
data = append(data, h.io3...)
data = append(data, HcIo4)
data = append(data, h.io4...)
data = append(data, HcIo5)
data = append(data, h.io5...)
data = append(data, HcPwm1)
data = append(data, h.pwm1.encode()...)
data = append(data, HcPwm2)
data = append(data, h.pwm2.encode()...)
data = append(data, HcPwm3)
data = append(data, h.pwm3.encode()...)
data = append(data, HcPwm4)
data = append(data, h.pwm4.encode()...)
data = append(data, HcPwm5)
data = append(data, h.pwm5.encode()...)
data = append(data, HcPwm6)
data = append(data, h.pwm6.encode()...)
data = append(data, HcAnalog1)
data = binary.BigEndian.AppendUint16(data, h.analog1)
data = append(data, HcAnalog2)
data = binary.BigEndian.AppendUint16(data, h.analog2)
data = append(data, HcAnalog3)
data = binary.BigEndian.AppendUint16(data, h.analog3)
data = append(data, HcAnalog4)
data = binary.BigEndian.AppendUint16(data, h.analog4)
data = append(data, HcAnalog5)
data = binary.BigEndian.AppendUint16(data, h.analog5)
data = append(data, HcAnalog6)
data = binary.BigEndian.AppendUint16(data, h.analog6)
data = append(data, h.cd.encode()...)
data = append(data, h.output422.encode()...)
data = append(data, h.output485.encode()...)
data = escape(data)
binary.BigEndian.PutUint16(data[1:], uint16(len(data)-3)) //填充帧长度
data = binary.BigEndian.AppendUint32(data, crc32(data))
data = append(data, FrameTail)
return data
}
// SC采集帧
type scCollectFrame struct {
timestamp scTimestampPackage
io1 scIoPackage
io2 scIoPackage
io3 scIoPackage
io4 scIoPackage
io5 scIoPackage
pwm1 scPwmPackage
pwm2 scPwmPackage
analog1 scAnalogPackage
analog2 scAnalogPackage
}
func (s *scCollectFrame) decode(frame []byte) error {
if frame[0] != FrameHead {
return errors.New(fmt.Sprintf("SC采集帧解析失败帧头不对%x", frame[0]))
}
if uint16(len(frame)-8) != binary.BigEndian.Uint16(frame[1:3]) {
return errors.New(fmt.Sprintf("SC采集帧解析失败数据长度不对期望[%x] 实际[%x]", binary.BigEndian.Uint16(frame[1:3]), len(frame)))
}
if binary.BigEndian.Uint32(frame[len(frame)-5:len(frame)-1]) != crc32(frame[:len(frame)-5]) {
return errors.New(fmt.Sprintf("SC采集帧解析失败CRC校验不通过期望[%x] 实际[%x]",
crc32(frame[:len(frame)-5]), binary.BigEndian.Uint32(frame[len(frame)-5:len(frame)-1])))
}
start := 3
err := s.timestamp.decode(frame[start : start+5])
if err != nil {
return err
}
start += 5
err = s.io1.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.io2.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.io3.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.io4.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.io5.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.pwm1.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.pwm2.decode(frame[start : start+4])
if err != nil {
return err
}
start += 4
err = s.analog1.decode(frame[start : start+3])
if err != nil {
return err
}
start += 3
err = s.analog2.decode(frame[start : start+3])
if err != nil {
return err
}
return nil
}
var crcHashParam = crc.NewHash(&crc.Parameters{
Width: 32,
Polynomial: 0x1EDC6F41,
ReflectIn: false,
ReflectOut: false,
Init: 0,
FinalXor: 0,
})
func crc32(data []byte) uint32 {
return uint32(crcHashParam.CalculateCRC(data))
}
func escape(data []byte) []byte {
var escapeByte byte = 0x7D
var xorByte byte = 0x20
newData := make([]byte, 0, len(data))
newData = append(newData, data[:3]...)
for _, b := range data[3:] {
if b <= FrameTail && b >= escapeByte {
newData = append(newData, escapeByte)
newData = append(newData, b^xorByte)
} else {
newData = append(newData, b)
}
}
return newData
}

View File

@ -0,0 +1,316 @@
package beijing11
import (
"encoding/binary"
"errors"
"fmt"
)
const (
FrameHead byte = 0x7E
FrameTail byte = 0x7F
)
type PackHead = byte
const (
HcTimestamp PackHead = iota + 1 //HC的时间戳
HcModeInfo //模式信息包
HcTimeSync //校时信息包
HcReqScSelfCheck //索要SC自检信息包
HcIo1 //开关量1输出控制信息包
HcIo2 //开关量2输出控制信息包
HcIo3 //开关量3输出控制信息包
HcIo4 //开关量4输出控制信息包
HcIo5 //开关量5输出控制信息包
HcPwm1 //PWM1输出控制信息包
HcPwm2 //PWM2输出控制信息包
HcPwm3 //PWM3输出控制信息包
HcPwm4 //PWM4输出控制信息包
HcPwm5 //PWM5输出控制信息包
HcPwm6 //PWM6输出控制信息包
HcAnalog1 //模拟量1输出控制信息包
HcAnalog2 //模拟量2输出控制信息包
HcAnalog3 //模拟量3输出控制信息包
HcAnalog4 //模拟量4输出控制信息包
HcAnalog5 //模拟量5输出控制信息包
HcAnalog6 //模拟量6输出控制信息包
HcCD //CD信号输出控制信息包
Hc422 //422信号输出控制信息包
Hc485 //485信号输出控制信息包
ScTimestamp //SC的时间戳
ScModeConfirm //模式确认信息包
ScTimeSyncConfirm //校时确认包
ScSelfCheckConfirm //SC自检信息包
ScIo1 //开关量1输出控制信息包
ScIo2 //开关量2输出控制信息包
ScIo3 //开关量3输出控制信息包
ScIo4 //开关量4输出控制信息包
ScIo5 //开关量5输出控制信息包
ScPwm1 //PWM1输出控制信息包
ScPwm2 //PWM2输出控制信息包
ScAnalog1 //模拟量1输出控制信息包
ScAnalog2 //模拟量2输出控制信息包
ScFaultInfo //故障信息包
)
type Mode = byte
const (
Invalid Mode = 0
One = 1 //模式一(小型化型式试验)
)
type PwmMode = byte
const (
PwmMode_Invalid PwmMode = iota //无效
PwmMode_One //输出模式11、2、3路PWM为一组4、5、6路PWM为另一组每组的3路PWM频率占空比相同相位互差120°
PwmMode_Two //输出模式21、2路PWM为一组3、45、6路PWM为另外两组每组的2路PWM频率占空比相同相位互差180°
PwmMode_Three //输出模式31、2、3、4、5、6路PWM可独立输出每路的频率占空比相位不会相互约束。
)
type hcTimestampPackage struct {
timestamp uint32
}
func (h *hcTimestampPackage) packHead() PackHead {
return HcTimestamp
}
func (h *hcTimestampPackage) encode() []byte {
data := make([]byte, 0, 5)
data = append(data, h.packHead())
data = binary.BigEndian.AppendUint32(data, h.timestamp)
return data
}
type modeInfoPackage struct {
mode Mode
}
func (m *modeInfoPackage) packHead() PackHead {
return HcModeInfo
}
func (m *modeInfoPackage) encode() []byte {
data := make([]byte, 0, 2)
data = append(data, m.packHead())
data = append(data, m.mode)
return data
}
type timeSyncInfoPackage struct {
hour byte
minute byte
second byte
}
func (t *timeSyncInfoPackage) packHead() PackHead {
return HcTimeSync
}
func (t *timeSyncInfoPackage) encode() []byte {
data := make([]byte, 0, 4)
data = append(data, t.packHead())
data = append(data, t.hour)
data = append(data, t.minute)
data = append(data, t.second)
return data
}
type reqScSelfCheckInfoPackage struct {
on bool
}
func (r *reqScSelfCheckInfoPackage) packHead() PackHead {
return HcReqScSelfCheck
}
func (r *reqScSelfCheckInfoPackage) encode() []byte {
data := make([]byte, 0, 2)
data = append(data, r.packHead())
if r.on {
data = append(data, 0xAA)
}
return data
}
type hcPwmControlPackage struct {
mode PwmMode
frequency uint16
duty byte
}
func (p *hcPwmControlPackage) encode() []byte {
data := make([]byte, 0, 4)
data = append(data, p.mode)
data = binary.BigEndian.AppendUint16(data, p.frequency)
data = append(data, p.duty)
return data
}
type hcCDControlPackage struct {
value byte
}
func (c *hcCDControlPackage) headPack() byte {
return HcCD
}
func (c *hcCDControlPackage) encode() []byte {
data := make([]byte, 0, 2)
data = append(data, c.headPack())
data = append(data, c.value)
return data
}
type hc422ControlPackage struct {
id byte
value []byte //长度256
}
func (c *hc422ControlPackage) headPack() byte {
return Hc422
}
func (c *hc422ControlPackage) encode() []byte {
data := make([]byte, 0, 258)
data = append(data, c.headPack())
data = append(data, c.id)
data = append(data, c.value...)
return data
}
type hc485ControlPackage struct {
id byte
value []byte //长度256
}
func (c *hc485ControlPackage) headPack() byte {
return Hc485
}
func (c *hc485ControlPackage) encode() []byte {
data := make([]byte, 0, 258)
data = append(data, c.headPack())
data = append(data, c.id)
data = append(data, c.value...)
return data
}
type scTimestampPackage struct {
timestamp uint32
}
func (s *scTimestampPackage) decode(data []byte) error {
if len(data) != 5 || data[0] != ScTimestamp {
return errors.New(fmt.Sprintf("数据有误无法解析为Sc时间戳信息包%x", data))
}
s.timestamp = binary.BigEndian.Uint32(data[1:])
return nil
}
type scModeConfirmPackage struct {
mode Mode
}
func (s *scModeConfirmPackage) decode(data []byte) error {
if len(data) != 2 || data[0] != ScModeConfirm {
return errors.New(fmt.Sprintf("数据有误,无法解析为模式确认信息包:%x", data))
}
s.mode = data[1]
return nil
}
type scTimeSyncConfirmPackage struct {
hour byte
minute byte
second byte
}
func (s *scTimeSyncConfirmPackage) decode(data []byte) error {
if len(data) != 4 || data[0] != ScTimeSyncConfirm {
return errors.New(fmt.Sprintf("数据有误,无法解析为时钟同步确认信息包:%x", data))
}
s.hour = data[1]
s.minute = data[2]
s.second = data[3]
return nil
}
type scSelfCheckConfirmPackage struct {
result byte
}
func (s *scSelfCheckConfirmPackage) decode(data []byte) error {
if len(data) != 2 || data[0] != ScSelfCheckConfirm {
return errors.New(fmt.Sprintf("数据有误,无法解析为自检确认信息包:%x", data))
}
s.result = data[1]
return nil
}
func (s *scSelfCheckConfirmPackage) isNormal() bool {
return s.result == 0xAA
}
func (s *scSelfCheckConfirmPackage) isAbnormal() bool {
return s.result == 0x55
}
type scIoPackage struct {
packHead byte
value []byte //三个字节
}
func (s *scIoPackage) decode(data []byte) error {
if len(data) != 4 || data[0] < ScIo1 || data[0] > ScIo5 {
return errors.New(fmt.Sprintf("数据有误无法解析为IO信息包%x", data))
}
s.packHead = data[0]
s.value = data[1:]
return nil
}
type scPwmPackage struct {
packHead byte
frequency uint16
duty byte
}
func (s *scPwmPackage) decode(data []byte) error {
if len(data) != 4 || data[0] < ScPwm1 || data[0] > ScPwm2 {
return errors.New(fmt.Sprintf("数据有误无法解析为PWM信息包%x", data))
}
s.packHead = data[0]
s.frequency = binary.BigEndian.Uint16(data[1:3])
s.duty = data[3]
return nil
}
type scAnalogPackage struct {
packHead byte
current uint16
}
func (s *scAnalogPackage) decode(data []byte) error {
if len(data) != 3 || data[0] < ScAnalog1 || data[0] > ScAnalog2 {
return errors.New(fmt.Sprintf("数据有误,无法解析为模拟量信息包:%x", data))
}
s.packHead = data[0]
s.current = binary.BigEndian.Uint16(data[1:])
return nil
}
type scFaultPackage struct {
selfCheck bool //自检是否正常
selfCheckBeforeControl bool //开机时是否未收到索要自检信息包却收到上位机控制命令
timeSync bool //时分秒校时是否正常
output bool //输出反馈是否正常
hcFrameFormatCorrect bool //收到HC帧格式是否正确
hcTimestamp bool //按照协议规定检测HC时间戳(ms)是否错误
crc bool //收到的信息CRC检验错误累计是否超过3次超过3次为异常
other bool //是否发生除上述情况外的其他故障
}

View File

@ -0,0 +1,432 @@
package beijing11
import (
"context"
"fmt"
"joylink.club/bj-rtsts-server/dto/state_proto"
"joylink.club/bj-rtsts-server/third_party/tpapi"
"joylink.club/bj-rtsts-server/third_party/udp"
"joylink.club/bj-rtsts-server/ts/simulation/wayside/memory"
"log"
"log/slog"
"runtime/debug"
"sync"
"time"
)
var (
logTag = "[北京11号线工装通信]"
privateLogger *slog.Logger
loggerInit sync.Once
)
var (
mu = sync.Mutex{} //启动任务时使用,避免重复启动任务
serviceCtx *serviceContext //当前正在运行的服务
)
type serviceContext struct {
simulation *memory.VerifySimulation
trainState *state_proto.TrainState
cancelFunc context.CancelFunc
client udp.UdpClient
server udp.UdpServer
startTime time.Time //服务启动时间
initFrame *hcInitFrame
initComplete bool
lastTime time.Time //接收到最后一条消息的时间
state tpapi.ThirdPartyApiServiceState
}
func (s *serviceContext) Type() state_proto.SimulationThirdPartyApiService_Type {
return state_proto.SimulationThirdPartyApiService_Train_pc_sim
}
func (s *serviceContext) State() tpapi.ThirdPartyApiServiceState {
return s.state
}
func (s *serviceContext) FindAppendApiService() []tpapi.ThirdPartyApiService {
return nil
}
func (s *serviceContext) TrueService() bool {
return true
}
func (s *serviceContext) ServiceDesc() string {
return logTag
}
func Start(simulation *memory.VerifySimulation) {
config := simulation.GetRunConfig().BtmVobc
if !config.Open {
return
}
mu.Lock()
defer mu.Unlock()
ctx, cancelFunc := context.WithCancel(context.Background())
client := udp.NewClient(fmt.Sprintf("%s:%d", config.RemoteIp, config.RemoteUdpPort))
serviceCtx = &serviceContext{
simulation: simulation,
cancelFunc: cancelFunc,
client: client,
startTime: time.Now(),
}
udp.NewServer(fmt.Sprintf(":%d", config.LocalUdpPort), serviceCtx.handle)
serviceCtx.runSendTask(ctx)
logger().Info("与工装通信服务启动完毕")
}
func Stop(simulation *memory.VerifySimulation) {
if serviceCtx.simulation == simulation {
serviceCtx.cancelFunc()
if serviceCtx.client != nil {
serviceCtx.client.Close()
}
if serviceCtx.server != nil {
serviceCtx.server.Close()
}
serviceCtx = nil
logger().Info("与工装通信服务关闭")
}
}
func (s *serviceContext) handle(data []byte) {
now := time.Now()
if !s.initComplete {
frame := &scInitFrame{}
err := frame.decode(data)
if err == nil {
return
}
s.lastTime = now
s.initComplete = true
} else {
frame := &scCollectFrame{}
err := frame.decode(data)
if err == nil {
return
}
//校验时间
if now.Sub(s.lastTime).Milliseconds() > 200 {
logger().Error("SC消息超时")
}
s.lastTime = now
//更新列车驾驶台状态
s.handleIo(frame)
}
}
// 由于本系统只有一组控制台按钮所以SC反馈的状态只有激活端的生效
func (s *serviceContext) handleIo(frame *scCollectFrame) {
sameBytes := make([]byte, 0, 4)
graphicData := memory.FindTrainTccGraphicData(s.simulation)
var end1 bool
for _, key := range s.trainState.Tcc.DriverKey {
if key.Val {
knob, find := memory.FindTrainTccGraphicDataKey(graphicData, key.Id)
if !find {
return
}
if knob.Code == memory.SKQYS1 {
end1 = true
sameBytes = append(sameBytes, frame.io1.value[0], frame.io1.value[1])
sameBytes = append(sameBytes, frame.io2.value...)
} else {
sameBytes = append(sameBytes, frame.io3.value[0], frame.io3.value[1])
sameBytes = append(sameBytes, frame.io4.value...)
}
break
}
}
if len(sameBytes) == 0 { //未找到激活端
return
}
//io1/io3-byte1
//ATO使能输出2(安全)/ATO使能输出1(安全)缺失ps下面还有一个“ATO已激活”的点位
s.trainState.VobcState.TrainStartedLed = sameBytes[0]&1<<4 != 0 //车启动灯
s.trainState.VobcState.NoSpeedSigle = sameBytes[0]&1<<3 != 0 //零速信号
//右门解锁(缺失)
//左门解锁(缺失)
s.trainState.VobcState.TrainTractionCuted = sameBytes[0]&1 == 0 //牵引切断
//io1/io3-byte2
s.trainState.VobcState.EmergencyBrakingStatus = sameBytes[1]&1<<5 == 0 && sameBytes[1]&1<<4 == 0 //1端紧急2/1端紧急1
s.trainState.VobcState.WakeUpBtn = sameBytes[1]&1<<3 != 0 //唤醒输出
s.trainState.VobcState.SleepBtn = sameBytes[1]&1<<2 != 0 //休眠输出
//io2/io4-byte1
//空气制动隔离输出(缺失)
//跳跃指令(缺失)
s.trainState.VobcState.Cam = sameBytes[2]&1<<5 != 0 //CAM输出
s.trainState.VobcState.Fam = sameBytes[2]&1<<4 != 0 //FAM输出
s.trainState.VobcState.DirectionBackward = sameBytes[2]&1<<3 != 0 //方向向后指令
s.trainState.VobcState.DirectionForward = sameBytes[2]&1<<2 != 0 //方向向前指令
s.trainState.VobcState.LightDriverActive = true //驾驶室激活输出
if end1 {
s.trainState.VobcState.Tc1Active = true
s.trainState.VobcState.Tc2Active = false
} else {
s.trainState.VobcState.Tc1Active = false
s.trainState.VobcState.Tc2Active = true
}
//VBTC对端重启指令缺失
//io2/io4-byte2
s.trainState.VobcState.LeftDoorCloseCommand = sameBytes[3]&1<<7 != 0 //全列车左车门关闭输出(姑且认为全列车门==客室车门吧)
s.trainState.VobcState.LeftDoorOpenCommand = sameBytes[3]&1<<6 != 0 //客室左车门开启输出
s.trainState.VobcState.MaintainBrakeStatus = sameBytes[3]&1<<5 != 0 //保持制动
s.trainState.VobcState.BrakeEffective = sameBytes[3]&1<<4 != 0 //制动状态
s.trainState.VobcState.TractionStatus = sameBytes[3]&1<<3 != 0 //牵引状态
s.trainState.VobcState.Ato = sameBytes[3]&1<<2 != 0 //ATO已激活
s.trainState.VobcState.StopNotAllBrake = sameBytes[3]&1<<1 != 0 //停车制动缓解输出
s.trainState.VobcState.ParkingBrakeStatus = sameBytes[3]&1<<0 != 0 //停车制动施加输出
//io2/io4-byte3
//行李车门右门使能
//行李车门左门使能
s.trainState.VobcState.RightDoorCloseCommand = sameBytes[4]&1<<1 != 0 //全列车右车门关闭输出
s.trainState.VobcState.RightDoorOpenCommand = sameBytes[4]&1 != 0 //客室右车门开启输出
}
func (s *serviceContext) runSendTask(ctx context.Context) {
go func() {
defer func() {
if err := recover(); err != nil {
logger().Error("数据发送任务出错,记录后重启", "error", err, "stack", string(debug.Stack()))
s.runSendTask(ctx)
}
}()
for range time.Tick(50 * time.Millisecond) {
select {
case <-ctx.Done():
return
default:
if !s.initComplete {
frame := s.buildHcInitFrame()
s.send(frame.encode())
} else {
frame := s.buildHcControlFrame()
s.send(frame.encode())
}
}
}
}()
}
func (s *serviceContext) buildHcInitFrame() *hcInitFrame {
frame := &hcInitFrame{}
now := time.Now()
frame.timestamp.timestamp = uint32(now.Sub(s.startTime).Milliseconds())
frame.modeInfo.mode = One
frame.timeSyncInfo.hour = byte(now.Hour())
frame.timeSyncInfo.minute = byte(now.Minute())
frame.timeSyncInfo.second = byte(now.Second())
frame.reqScSelfCheckInfo.on = true
return frame
}
func (s *serviceContext) buildHcControlFrame() *hcControlFrame {
frame := &hcControlFrame{}
now := time.Now()
frame.timestamp.timestamp = uint32(now.Sub(s.startTime).Milliseconds())
graphicData := memory.FindTrainTccGraphicData(s.simulation)
bytes1 := s.buildActivationIoBytes()
bytes2 := s.buildUnactivatedIoBytes()
var findActivation bool
for _, key := range s.trainState.Tcc.DriverKey {
if key.Val {
findActivation = true
knob, find := memory.FindTrainTccGraphicDataKey(graphicData, key.Id)
if !find {
return nil
}
if knob.Code == memory.SKQYS1 {
frame.io1 = bytes1[:3]
frame.io2 = bytes1[3:6]
frame.io3 = bytes2[:3]
frame.io4 = bytes2[3:6]
frame.io5 = []byte{bytes1[6], bytes2[6], 0}
} else {
frame.io1 = bytes2[:3]
frame.io2 = bytes2[3:6]
frame.io3 = bytes1[:3]
frame.io4 = bytes1[3:6]
frame.io5 = []byte{bytes2[6], bytes1[6], 0}
}
break
}
}
if !findActivation {
return nil
}
frame.pwm1 = hcPwmControlPackage{One, 0, 0}
frame.pwm2 = hcPwmControlPackage{One, 0, 0}
frame.pwm3 = hcPwmControlPackage{One, 0, 0}
frame.pwm4 = hcPwmControlPackage{One, 0, 0}
frame.pwm5 = hcPwmControlPackage{One, 0, 0}
frame.pwm6 = hcPwmControlPackage{One, 0, 0}
frame.analog1 = 0
frame.analog2 = 0
frame.analog3 = 0
frame.analog4 = 0
frame.analog5 = 0
frame.analog6 = 0
return frame
}
func (s *serviceContext) send(data []byte) {
if err := s.client.Send(data); err != nil {
log.Println("数据发送任务出错", "error", err)
}
if err := s.client.Send(data); err != nil {
log.Println("数据发送任务出错", "error", err)
}
}
// 构建激活端io字节数组
func (s *serviceContext) buildActivationIoBytes() []byte {
data := make([]byte, 7) //前6个字节对应io1/io2或者io3/io4第7个字节对应激活端的io5
graphicData := memory.FindTrainTccGraphicData(s.simulation)
if graphicData == nil {
return data
}
handlerZero := s.trainState.Tcc.PushHandler.Val == 0 //操纵杆在零位
front := s.trainState.Tcc.SwitchKeyMap[memory.QHFXKZ].Val == 1 //方向手柄在向前位
//io1/io3-byte3
if handlerZero && front { //牵引制动手柄在零位且方向手柄在向前位
data[2] += 1 << 7
}
if s.trainState.Tcc.LightMaps[memory.LIGHT_TFZDSJ].Val { //车辆保持制动已实施(是停车制动吗?)
data[2] += 1 << 6
}
if !s.trainState.Tcc.Buttons[memory.JJZD].Passed { //车辆已实施紧急制动(低电平有效)
data[2] += 1 << 5
}
if s.trainState.Tcc.LightMaps[memory.LIGHT_QQY].Val { //牵引已切除(高电平有效)
data[2] += 1 << 4
}
data[2] += 1 << 3 //车门关闭且锁闭
data[2] += 1 << 2 //列车完整性
data[2] += 1 << 1 //司机钥匙激活信号
data[2] += 1 //驾驶室激活状态
//io1/io3-byte2
if s.trainState.Tcc.Buttons[memory.ATOQD].Passed { //ATO启动按钮2已按下
data[1] += 1 << 7
}
if s.trainState.Tcc.Buttons[memory.LIGHT_ATO_CLOSE_LEFT_DOOR].Passed { //左门关按钮按下
data[1] += 1 << 6
}
if s.trainState.Tcc.Buttons[memory.LIGHT_ATO_OPEN_LEFT_DOOR].Passed { //左门开按钮按下
data[1] += 1 << 5
}
data[1] += 1 << 3 //车辆紧急手柄激活(低电平有效)
if s.trainState.Tcc.SwitchKeyMap[memory.QHFXKZ].Val == 0 { //方向手柄向后
data[1] += 1 << 2
}
data[1] += 1 << 1 //逃生门状态
if s.trainState.Tcc.Buttons[memory.MSQR].Passed { //确认按钮状态(是模式确认吗?)
data[1] += 1
}
//io1/io3-byte1
data[0] = data[2]
//io2/io4-byte3
if handlerZero && front { //牵引制动手柄在牵引位且方向向前
data[5] += 1 << 6
}
if !s.trainState.Tcc.Buttons[memory.ZDZGZ].Passed { //制动重故障(低电平有效)
data[5] += 1 << 5
}
if !s.trainState.Tcc.Buttons[memory.ZAWTGJC].Passed { //障碍物脱轨有效(低电平有效)
data[5] += 1 << 4
}
//EUM开关激活省略
if s.trainState.Tcc.Buttons[memory.LIGHT_ATO_CLOSE_RIGHT_DOOR].Passed { //右门关门按钮按下
data[5] += 1 << 2
}
if s.trainState.Tcc.Buttons[memory.LIGHT_ATO_OPEN_RIGHT_DOOR].Passed { //右门开门按钮按下
data[5] += 1 << 1
}
if s.trainState.Tcc.Buttons[memory.ZF].Passed { //AR按钮按下
data[5] += 1
}
//io2/io4-byte2
if s.trainState.Tcc.Buttons[memory.MSJJ].Passed { //模式选择下按钮按下
data[4] += 1 << 7
}
if s.trainState.Tcc.Buttons[memory.MSSJ].Passed { //模式选择上按钮按下
data[4] += 1 << 6
}
if s.trainState.Tcc.Buttons[memory.ATOQD].Passed { //ATO启动按钮1已按下
data[4] += 1 << 5
}
if handlerZero && front { //牵引制动手柄在牵引位且方向向前
data[4] += 1 << 2
}
if s.trainState.Tcc.Buttons[memory.ZDZGZ].Passed { //制动重故障(低电平有效)
data[4] += 1 << 1
}
if s.trainState.Tcc.Buttons[memory.ZAWTGJC].Passed { //障碍物脱轨有效(低电平有效)
data[4] += 1
}
//io2/io4-byte1
switch s.trainState.Tcc.SwitchKeyMap[memory.MMS].Val {
case 0: //人工开人工关
data[3] += 1 << 5
case 1: //自动开自动关
data[3] += 1 << 6
case 2: //自动开人工关
data[3] += 1 << 7
}
//烟雾报警
data[3] += 1 << 3 //车辆紧急手柄激活(低电平有效)
if s.trainState.Tcc.SwitchKeyMap[memory.QHFXKZ].Val == 0 { //方向手柄向后
data[3] += 1 << 2
}
data[3] += 1 << 1 //逃生门状态
if s.trainState.Tcc.Buttons[memory.MSQR].Passed { //确认按钮状态(是模式确认吗?)
data[3] += 1
}
//io5-byte2/byte1
//低压上电检测(省略)
data[6] += 1 << 3 //车辆上电按钮
if s.trainState.Tcc.Buttons[memory.JX].Passed {
data[6] += 1 << 2
}
//蓄电池欠压保护输入(省略)
if s.trainState.Tcc.Buttons[memory.XM].Passed { //车辆休眠按钮
data[6] += 1
}
return data
}
// 构建未激活端io字节数组
func (s *serviceContext) buildUnactivatedIoBytes() []byte {
data := make([]byte, 7) //前6个字节对应io1/io2或者io3/io4第7个字节对应未激活端的io5
data[2] += 1 << 7 //牵引制动手柄在零位且方向手柄在向前位
data[2] += 1 << 5 //车辆已实施紧急制动(低电平有效)
data[2] += 1 << 3 //车门关闭且锁闭
data[2] += 1 << 2 //列车完整性
data[1] += 1 << 3 //车辆紧急手柄激活(低电平有效)
data[1] += 1 << 2 //逃生门状态
data[0] = data[2]
data[5] += 1 << 6 //牵引制动手柄在牵引位且方向向前
data[5] += 1 << 5 //制动重故障(低电平有效)
data[5] += 1 << 4 //障碍物脱轨有效(低电平有效)
data[4] += 1 << 2 //牵引制动手柄在牵引位且方向向前
data[4] += 1 << 1 //制动重故障(低电平有效)
data[4] += 1 //障碍物脱轨有效(低电平有效)
data[3] += 1 << 7 //自动开人工关
data[3] += 1 << 3 //车辆紧急手柄激活(低电平有效)
data[3] += 1 << 1 //逃生门状态
return data
}
func logger() *slog.Logger {
loggerInit.Do(func() {
privateLogger = slog.Default().With("tag", logTag)
})
return privateLogger
}

View File

@ -14,6 +14,7 @@ const (
QHFXKZ = "QHFXKZ" //驾驶方向
QYSB = "QYSB" //牵引制动手柄
ATOQD = "ATOQD" //ato 启动
MMS = "MMS" //门模式
ATPQCKG = "ATPQCKG" //ATP切除开关
KZM = "KZM" //开左门按钮
@ -54,7 +55,7 @@ const (
)
// 获取列车控制图形数据
func findTrainTccGraphicData(vs *VerifySimulation) *data_proto.TccGraphicStorage {
func FindTrainTccGraphicData(vs *VerifySimulation) *data_proto.TccGraphicStorage {
var tccGI *data_proto.TccGraphicStorage
for _, id := range vs.MapIds {
if QueryGiType(id) == data_proto.PictureType_TrainControlCab {
@ -64,41 +65,41 @@ func findTrainTccGraphicData(vs *VerifySimulation) *data_proto.TccGraphicStorage
}
return tccGI
}
func findTrainTccGraphicDataButtons(tccG *data_proto.TccGraphicStorage) []*data_proto.TccButton {
func FindTrainTccGraphicDataButtons(tccG *data_proto.TccGraphicStorage) []*data_proto.TccButton {
return tccG.TccButtons
}
func findTrainTccGraphicDataKeys(tccG *data_proto.TccGraphicStorage) []*data_proto.TccKey {
func FindTrainTccGraphicDataKeys(tccG *data_proto.TccGraphicStorage) []*data_proto.TccKey {
return tccG.TccKeys
}
func findTrainTccGraphicDataHandlers(tccG *data_proto.TccGraphicStorage) []*data_proto.TccHandle {
func FindTrainTccGraphicDataHandlers(tccG *data_proto.TccGraphicStorage) []*data_proto.TccHandle {
return tccG.TccHandles
}
func findTrainTccGraphicDataButtonByCode(tccG *data_proto.TccGraphicStorage, deviceCode string) (*data_proto.TccButton, bool) {
for _, d := range findTrainTccGraphicDataButtons(tccG) {
func FindTrainTccGraphicDataButtonByCode(tccG *data_proto.TccGraphicStorage, deviceCode string) (*data_proto.TccButton, bool) {
for _, d := range FindTrainTccGraphicDataButtons(tccG) {
if d.Code == deviceCode {
return d, true
}
}
return nil, false
}
func findTrainTccGraphicDataButton(tccG *data_proto.TccGraphicStorage, id uint32) (*data_proto.TccButton, bool) {
for _, d := range findTrainTccGraphicDataButtons(tccG) {
func FindTrainTccGraphicDataButton(tccG *data_proto.TccGraphicStorage, id uint32) (*data_proto.TccButton, bool) {
for _, d := range FindTrainTccGraphicDataButtons(tccG) {
if d.Common.Id == id {
return d, true
}
}
return nil, false
}
func findTrainTccGraphicDataKey(tccG *data_proto.TccGraphicStorage, id uint32) (*data_proto.TccKey, bool) {
for _, d := range findTrainTccGraphicDataKeys(tccG) {
func FindTrainTccGraphicDataKey(tccG *data_proto.TccGraphicStorage, id uint32) (*data_proto.TccKey, bool) {
for _, d := range FindTrainTccGraphicDataKeys(tccG) {
if d.Common.Id == id {
return d, true
}
}
return nil, false
}
func findTrainTccGraphicDataHandler(tccG *data_proto.TccGraphicStorage, id uint32) (*data_proto.TccHandle, bool) {
for _, d := range findTrainTccGraphicDataHandlers(tccG) {
func FindTrainTccGraphicDataHandler(tccG *data_proto.TccGraphicStorage, id uint32) (*data_proto.TccHandle, bool) {
for _, d := range FindTrainTccGraphicDataHandlers(tccG) {
if d.Common.Id == id {
return d, true
}

View File

@ -30,7 +30,7 @@ func ControlTrainUpdate(s *VerifySimulation, ct *request_proto.TrainControl) {
if !ok {
panic(sys_error.New(fmt.Sprintf("列车【%s】不存在", ct.TrainId)))
}
tccGraphicData := findTrainTccGraphicData(s)
tccGraphicData := FindTrainTccGraphicData(s)
if tccGraphicData == nil {
slog.Error("列车控制未找到TCC图形数据")
panic(sys_error.New("未找到TCC图形数据"))
@ -89,7 +89,7 @@ func ControlTrainUpdate(s *VerifySimulation, ct *request_proto.TrainControl) {
func trainControlButton(train *state_proto.TrainState, deviceId uint32, active bool, tccGraphic *data_proto.TccGraphicStorage) []message.TrainPcSimBaseMessage {
if graphicBtn, ok := findTrainTccGraphicDataButton(tccGraphic, deviceId); ok {
if graphicBtn, ok := FindTrainTccGraphicDataButton(tccGraphic, deviceId); ok {
btn := train.Tcc.Buttons[graphicBtn.Code]
if btn == nil {
slog.Error("未找到对应的车载摁钮code:", graphicBtn.Code, "设备id:", deviceId)
@ -356,7 +356,7 @@ func controlAtoSenderBtn(vobc *state_proto.TrainVobcState, active bool, tccBtn *
// 列车方向
func trainControlDirKey(trainSpeed int32, vobc *state_proto.TrainVobcState, tcc *state_proto.TrainControlState, request *request_proto.TrainControl_SwitchKeyChange, deviceId uint32, tccGraphic *data_proto.TccGraphicStorage) []message.TrainPcSimBaseMessage {
dirKey, find := findTrainTccGraphicDataKey(tccGraphic, deviceId)
dirKey, find := FindTrainTccGraphicDataKey(tccGraphic, deviceId)
if !find {
slog.Error("未找到对应的列车方向键deviceId:", deviceId)
panic(sys_error.New("未找到对应的列车方向键"))
@ -380,7 +380,7 @@ func trainControlDirKey(trainSpeed int32, vobc *state_proto.TrainVobcState, tcc
// 列车驾驶端激活
func trainControlDriverKey(train *state_proto.TrainState, request *request_proto.TrainControl_DriverKeySwitch, deviceId uint32, tccGraphic *data_proto.TccGraphicStorage) []message.TrainPcSimBaseMessage {
obj, find := findTrainTccGraphicDataKey(tccGraphic, deviceId)
obj, find := FindTrainTccGraphicDataKey(tccGraphic, deviceId)
if !find {
slog.Error("未找到对应的驾驶端激活设备deviceId:", deviceId)
return nil
@ -433,7 +433,7 @@ func trainControlDriverKey(train *state_proto.TrainState, request *request_proto
}
func trainDoorModeChangeHandle(vobc *state_proto.TrainVobcState, tcc *state_proto.TrainControlState, request *request_proto.TrainControl_SwitchKeyChange, deviceId uint32, tccGraphic *data_proto.TccGraphicStorage) []message.TrainPcSimBaseMessage {
sk, find := findTrainTccGraphicDataKey(tccGraphic, deviceId)
sk, find := FindTrainTccGraphicDataKey(tccGraphic, deviceId)
if !find {
slog.Error("未找到对应的牵引制动手柄设备deviceId:", deviceId)
return nil
@ -536,7 +536,7 @@ func trainAtoControlTractionAndBrake(train *state_proto.TrainState) {
// 列车牵引控制
func trainControlHandle(train *state_proto.TrainState, request *request_proto.TrainControl_PushHandler, deviceId uint32, tccGraphic *data_proto.TccGraphicStorage) []message.TrainPcSimBaseMessage {
_, find := findTrainTccGraphicDataHandler(tccGraphic, deviceId)
_, find := FindTrainTccGraphicDataHandler(tccGraphic, deviceId)
if !find {
slog.Error("未找到对应的牵引制动手柄设备deviceId:", deviceId)
return nil

View File

@ -9,6 +9,7 @@ import (
"joylink.club/bj-rtsts-server/third_party/interlock/beijing12"
"joylink.club/bj-rtsts-server/third_party/radar"
"joylink.club/bj-rtsts-server/third_party/train_pc_sim"
trainBeijing11 "joylink.club/bj-rtsts-server/third_party/train_pc_sim/beijing11"
"joylink.club/rtsssimulation/fi"
"log/slog"
"runtime"
@ -172,6 +173,8 @@ func runThirdParty(s *memory.VerifySimulation) error {
train_pc_sim.Default().Start(s)
//btm vobc
semi_physical_train.BtmDefault().Start(s)
//11号线工装通信
trainBeijing11.Start(s)
return nil
}
@ -207,6 +210,8 @@ func stopThirdParty(s *memory.VerifySimulation) {
//obsolete.StopLineAllRsspAxleServices()
// 电机UDP停止
electrical_machinery.Default().Stop()
//11号线工装通信
trainBeijing11.Stop(s)
}