[新增]11号线联锁通信所属线路配置;11号线联锁通信原始数据及解析后的json数据日志
All checks were successful
local-test分支打包构建docker并发布运行 / Docker-Build (push) Successful in 1m31s

This commit is contained in:
thesai 2024-05-29 17:20:51 +08:00
parent 61e60e4e64
commit cac309f4b1
7 changed files with 426 additions and 187 deletions

View File

@ -3,10 +3,9 @@ package config
import (
"flag"
"fmt"
"log/slog"
"github.com/spf13/viper"
"joylink.club/iot/dto"
"log/slog"
)
const (
@ -126,6 +125,7 @@ type InterlockConfig struct {
RemotePort int `json:"remotePort" description:"远端接收采集信息端口"`
Open bool `json:"open" description:"是否开启"`
Code string `json:"code" description:"所属集中站"`
Line string `json:"line" description:"联锁通信协议所属线路"`
}
type ElectricMachineryConfig struct {
Ip string `json:"ip" description:"IP配置"`

View File

@ -1,23 +0,0 @@
package beijing11
var (
FrameHead byte = 0x0E //帧头
FrameTail byte = 0x0E //帧尾
ControlWord = controlWord{
HeartBeat: 0x00,
ReadRegister: 0x01,
WriteRegister: 0x02,
Response: 0x03,
Upstream: 0x06,
DownStream: 0x07,
}
)
type controlWord struct {
HeartBeat byte //心跳
ReadRegister byte //读寄存器
WriteRegister byte //写寄存器
Response byte //回复
Upstream byte //上行流数据
DownStream byte //下行流数据
}

View File

@ -2,6 +2,7 @@
package beijing11
import (
"encoding/json"
"fmt"
"joylink.club/bj-rtsts-server/config"
"joylink.club/bj-rtsts-server/third_party/udp"
@ -10,117 +11,53 @@ import (
)
var (
initMutex = sync.Mutex{}
interlockMap = make(map[string]*InterlockProxy)
logTag = "[北京11号线联锁通信]"
initMutex = sync.Mutex{}
logTag = "[北京11号线联锁通信]"
running = false
server udp.UdpServer
)
func GetInstant(c *config.InterlockConfig, msgManager MsgManager) *InterlockProxy {
initMutex.Lock()
defer initMutex.Unlock()
if interlockMap[c.Code] == nil {
interlockMap[c.Code] = &InterlockProxy{runConfig: c, msgManager: msgManager}
}
return interlockMap[c.Code]
}
type MsgManager interface {
HandleReadRegisterMsg(req *ReadRegisterReq) (*ReadRegisterRes, error)
HandleWriteRegisterMsg(req *WriteRegisterReq) error
HandleDownstreamMsg(data []byte) error
CollectUpstreamMsg() []byte
}
type msgManager struct {
}
type InterlockProxy struct {
runConfig *config.InterlockConfig //联锁通信配置
msgManager MsgManager //消息获取与处理接口0
client udp.UdpClient //向上位机发送数据的UDP客户端
server udp.UdpServer //接收上位机数据的UDP服务端
running bool //此服务正在运行的标志
}
func (i *InterlockProxy) Start() {
if i.runConfig == nil || i.runConfig.Ip == "" || !i.runConfig.Open {
func Start(interlockConfig *config.InterlockConfig) {
if interlockConfig == nil || interlockConfig.Ip == "" || !interlockConfig.Open {
return
}
if i.running {
panic("启动联锁消息服务错误: 存在正在运行的任务")
if running {
return
}
initMutex.Lock()
defer initMutex.Unlock()
if running {
return
}
//UDP通信设施
i.server = udp.NewServer(fmt.Sprintf(":%d", i.runConfig.LocalPort), i.handleUpperData)
err := i.server.Listen()
server = udp.NewServer(fmt.Sprintf(":%d", interlockConfig.LocalPort), func(b []byte) {
slog.Info(fmt.Sprintf("%s收到消息%x", logTag, b))
frame := &FromInterlockFrame{}
err := frame.Decode(b)
if err != nil {
slog.Error(fmt.Sprintf("%s解析数据出错%s", logTag, err))
} else {
marshal, err := json.Marshal(frame)
if err != nil {
slog.Error(fmt.Sprintf("%s解析为json出错%s", logTag, err))
} else {
slog.Info(fmt.Sprintf("%s解析为json%s", logTag, string(marshal)))
}
}
})
err := server.Listen()
if err != nil {
panic("启动联锁消息服务错误无法启动UDP服务")
panic(fmt.Sprintf("%s启动UDP服务失败%s", logTag, err))
}
i.running = true
i.client = udp.NewClient(fmt.Sprintf("%v:%v", i.runConfig.Ip, i.runConfig.RemotePort))
running = true
}
// 处理来自上位机的数据
func (i *InterlockProxy) handleUpperData(data []byte) {
baseData := &BaseStruct{}
err := baseData.Decode(data)
if err != nil {
slog.Error(logTag + "数据解析出错:" + err.Error())
return
}
switch baseData.ControlWord {
case ControlWord.ReadRegister:
req := &ReadRegisterReq{}
err := req.Decode(baseData.Data)
if err != nil {
slog.Error(logTag + "读寄存器数据解析出错:" + err.Error())
return
}
res, err := i.msgManager.HandleReadRegisterMsg(req)
if err != nil {
slog.Error(logTag + "读寄存器数据处理出错:" + err.Error())
return
}
resData := &BaseStruct{
ID: baseData.ID,
ControlWord: ControlWord.Response,
Data: res.Encode(),
}
i.SendToUpper(resData.Encode())
case ControlWord.WriteRegister:
req := &WriteRegisterReq{}
err := req.Decode(baseData.Data)
if err != nil {
slog.Error(logTag + "写寄存器数据解析出错:" + err.Error())
return
}
err = i.msgManager.HandleWriteRegisterMsg(req)
if err != nil {
slog.Error(logTag + "写寄存器数据处理出错:" + err.Error())
return
}
case ControlWord.DownStream:
err = i.msgManager.HandleDownstreamMsg(baseData.Data)
if err != nil {
slog.Error(logTag + "下行数据处理出错:" + err.Error())
return
}
}
}
func (i *InterlockProxy) SendToUpper(data []byte) {
err := i.client.Send(data)
slog.Error(logTag + "向上位机发送数据失败:" + err.Error())
}
func (i *InterlockProxy) Stop() {
func Stop() {
initMutex.Lock()
defer initMutex.Unlock()
delete(interlockMap, i.runConfig.Code)
if i.client != nil {
i.client.Close()
running = false
if server != nil {
server.Close()
server = nil
}
if i.server != nil {
i.server.Close()
}
i.running = false
}

View File

@ -3,84 +3,337 @@ package beijing11
import (
"encoding/binary"
"fmt"
"hash/crc32"
"joylink.club/bj-rtsts-server/sys_error"
)
type BaseStruct struct {
ID byte
ControlWord byte
Data []byte
// FromInterlockFrame 来自联锁的数据帧
type FromInterlockFrame struct {
Len uint16
InterlockCode uint16 //联锁ID
WaysideCode uint16 //轨旁ID
TurnoutData *DeviceData //道岔数据
PSDData *DeviceData //屏蔽门数据
ESBData *ESBData //紧急停车按钮数据
HoldTrainData *HoldTrainData //扣车按钮数据
SignalData *DeviceData //信号机数据
AxleSectionData *DeviceData //计轴区段数据
WrzfData *DeviceData //无人折返数据
FymData *DeviceData //防淹门数据
SPKSData *SPKSData //SPKS数据
CkmData *DeviceData //车库门数据
XcjData *DeviceData //洗车机数据
}
func (m *BaseStruct) Decode(buf []byte) error {
if buf[0] != FrameHead {
return sys_error.New(logTag + "帧头不正确:" + fmt.Sprintf("%02X", buf[0]))
func (f *FromInterlockFrame) Decode(data []byte) error {
f.Len = binary.BigEndian.Uint16(data[1:3])
if int(f.Len) != len(data)-3 {
return sys_error.New(fmt.Sprintf("%s数据长度不对[%d:%d]", logTag, f.Len, len(data)-3))
}
if buf[len(buf)-1] != FrameTail {
return sys_error.New(logTag + "帧尾不正确:" + fmt.Sprintf("%02X", buf[len(buf)-1]))
}
crc := crc32.ChecksumIEEE(buf[1 : len(buf)-5])
if crc != binary.BigEndian.Uint32(buf[len(buf)-5:len(buf)-1]) {
return sys_error.New(logTag + "CRC校验失败")
}
m.ID = buf[1]
m.ControlWord = buf[2]
m.Data = buf[5 : len(buf)-5]
f.InterlockCode = binary.BigEndian.Uint16(data[3:5])
f.WaysideCode = binary.BigEndian.Uint16(data[5:7])
turnoutData, i := decodeDeviceData(data, 7)
f.TurnoutData = turnoutData
psdData, i := decodeDeviceData(data, i)
f.PSDData = psdData
esbData, i := decodeESBData(data, i)
f.ESBData = esbData
holdTrainData, i := decodeHoldTrainData(data, i)
f.HoldTrainData = holdTrainData
signalData, i := decodeDeviceData(data, i)
f.SignalData = signalData
axleSectionData, i := decodeDeviceData(data, i)
f.AxleSectionData = axleSectionData
wrzfData, i := decodeDeviceData(data, i)
f.WrzfData = wrzfData
fymData, i := decodeDeviceData(data, i)
f.FymData = fymData
spksData, i := decodeSPKSData(data, i)
f.SPKSData = spksData
ckmData, i := decodeDeviceData(data, i)
f.CkmData = ckmData
xcjData, i := decodeDeviceData(data, i)
f.XcjData = xcjData
return nil
}
func (m *BaseStruct) Encode() []byte {
var data []byte
data = append(data, FrameHead)
data = append(data, m.ID)
data = append(data, m.ControlWord)
data = binary.BigEndian.AppendUint16(data, uint16(len(m.Data)))
data = append(data, m.Data...)
crc := crc32.ChecksumIEEE(data[1:])
data = binary.BigEndian.AppendUint32(data, crc)
data = append(data, FrameTail)
return data
}
type ReadRegisterReq struct {
RegisterAddr []uint16 //寄存器地址
}
func (r *ReadRegisterReq) Decode(buf []byte) error {
if len(buf)%2 != 0 {
return sys_error.New(logTag + "读寄存器数据字节长度不是2的倍数")
func decodeDeviceData(data []byte, index uint16) (*DeviceData, uint16) {
data = data[index:]
num := binary.BigEndian.Uint16(data[0:2])
i := 2 + 3*num
data = data[2:i]
var cmdList []*DeviceCmd
for i := 0; i < len(data); i += 3 {
cmdList = append(cmdList, &DeviceCmd{
Id: binary.BigEndian.Uint16(data[i : i+2]),
Cmd: data[i+2],
})
}
for i := 0; i < len(buf); i += 2 {
r.RegisterAddr = append(r.RegisterAddr, binary.BigEndian.Uint16(buf[i:i+2]))
deviceData := &DeviceData{
Num: num,
CmdList: cmdList,
}
return nil
return deviceData, index + i
}
type ReadRegisterRes struct {
RegisterData [][]byte //寄存器数据
func decodeESBData(data []byte, index uint16) (*ESBData, uint16) {
data = data[index:]
num := binary.BigEndian.Uint16(data[0:2])
nextIndex := 2 + 6*num
data = data[2:nextIndex]
var cmdList []*ESBCmd
for i := 0; i < len(data); i += 6 {
cmdList = append(cmdList, &ESBCmd{
Id: binary.BigEndian.Uint16(data[i : i+2]),
JjtcCmd: data[i+2],
JjtcplCmd: data[i+3],
JjtcfmqCmd: data[i+4],
Ess1Cmd: data[i+5],
})
}
deviceData := &ESBData{
Num: num,
CmdList: cmdList,
}
return deviceData, index + nextIndex
}
func (r *ReadRegisterRes) Encode() []byte {
var data []byte
for _, datum := range r.RegisterData {
data = append(data, datum...)
func decodeHoldTrainData(data []byte, index uint16) (*HoldTrainData, uint16) {
data = data[index:]
num := binary.BigEndian.Uint16(data[0:2])
nextIndex := 2 + 6*num
data = data[2:nextIndex]
var cmdList []*HoldTrainCmd
for i := 0; i < len(data); i += 6 {
cmdList = append(cmdList, &HoldTrainCmd{
Id: binary.BigEndian.Uint16(data[i : i+2]),
KcCmd: data[i+2],
KcplCmd: data[i+3],
Kc2Cmd: data[i+4],
Kcpl2Cmd: data[i+5],
})
}
return data
deviceData := &HoldTrainData{
Num: num,
CmdList: cmdList,
}
return deviceData, index + nextIndex
}
type WriteRegisterReq struct {
RegisterAddr []uint16 //寄存器地址
RegisterData [][]byte //寄存器数据
func decodeSPKSData(data []byte, index uint16) (*SPKSData, uint16) {
data = data[index:]
num := binary.BigEndian.Uint16(data[0:2])
nextIndex := 2 + 4*num
data = data[2:nextIndex]
var cmdList []*SPKSCmd
for i := 0; i < len(data); i += 4 {
cmdList = append(cmdList, &SPKSCmd{
Id: binary.BigEndian.Uint16(data[i : i+2]),
SPKSCmd: data[i+2],
SPKSPLCmd: data[i+3],
})
}
deviceData := &SPKSData{
Num: num,
CmdList: cmdList,
}
return deviceData, index + nextIndex
}
func (w *WriteRegisterReq) Decode(buf []byte) error {
if len(buf)%6 != 0 {
return sys_error.New(logTag + "写寄存器数据字节长度不是6的倍数")
}
for i := 0; i < len(buf); i += 6 {
w.RegisterAddr = append(w.RegisterAddr, binary.BigEndian.Uint16(buf[i:i+2]))
w.RegisterData = append(w.RegisterData, buf[i+2:i+6])
}
return nil
type DeviceData struct {
Num uint16 //设备数量
CmdList []*DeviceCmd
}
type ESBData struct {
Num uint16 //紧急停车按钮的数量
CmdList []*ESBCmd
}
type HoldTrainData struct {
Num uint16 //扣车的数量
CmdList []*HoldTrainCmd
}
type SPKSData struct {
Num uint16 //SPKS的数量
CmdList []*SPKSCmd
}
type ESBCmd struct {
Id uint16 //紧急停车ID
JjtcCmd byte //紧急停车按钮命令
JjtcplCmd byte //紧急停车旁路按钮命令
JjtcfmqCmd byte //紧急停车蜂鸣器命令
Ess1Cmd byte //Ess1指示灯状态
}
type HoldTrainCmd struct {
Id uint16 //扣车ID
KcCmd byte //扣车命令
KcplCmd byte //扣车旁路命令
Kc2Cmd byte //2号扣车命令
Kcpl2Cmd byte //2号扣车旁路命令
}
type SPKSCmd struct {
Id uint16 //SPKS ID
SPKSCmd byte //SPKS按钮
SPKSPLCmd byte //SPKSPL按钮
}
type DeviceCmd struct {
Id uint16 //设备ID
Cmd byte //设备命令
}
// ToInterlockFrame 发往联锁的数据帧
type ToInterlockFrame struct {
Len uint16
WaysideCode uint16
InterlockCode uint16
TurnoutData *ToInterlockTurnoutData //道岔数据
PSDData *ToInterlockPSDData //屏蔽门数据
ESBData *ToInterlockESBData //紧急停车按钮数据
HoldTrainData *ToInterlockHoldTrainData //扣车按钮数据
SignalData *ToInterlockSignalData //信号机数据
AxleSectionData *ToInterlockAxleSectionData //计轴区段数据
WrzfData *ToInterlockWrzfData //无人折返数据
FymData *ToInterlockFymData //防淹门数据
SPKSData *ToInterlockSPKSData //SPKS数据
CkmData *ToInterlockCkmData //车库门数据
XcjData *ToInterlockXcjData //洗车机数据
}
type ToInterlockTurnoutData struct {
Num uint16 //该联锁集中区包含的道岔数量
StateList []*TurnoutState
}
type ToInterlockPSDData struct {
Num uint16 //该联锁集中区包含的屏蔽门数量
StateList []*PSDState
}
type ToInterlockESBData struct {
Num uint16 //紧急停车按钮数量
StateList []*ESBState
}
type ToInterlockHoldTrainData struct {
Num uint16 //扣车数量
StateList []*HoldTrainState
}
type ToInterlockSignalData struct {
Num uint16 //该联锁集中区信号机的数量
StateList []*SignalState
}
type ToInterlockAxleSectionData struct {
Num uint16 //该联锁集中区计轴区段的数量
StateList []*AxleSectionState
}
type ToInterlockWrzfData struct {
Num uint16 //该联锁集中区无人折返的数量
StateList []*WrzfState
}
type ToInterlockFymData struct {
Num uint16 //该联锁集中区防<E58CBA><E998B2>门的数量
StateList []*FymState
}
type ToInterlockSPKSData struct {
Num uint16 //SPKS数量
StateList []*SPKSState
}
type ToInterlockCkmData struct {
Num uint16 //车库门数量
StateList []*CkmState
}
type ToInterlockXcjData struct {
Num uint16 //洗车机数量
StateList []*XcjState
}
type TurnoutState struct {
Id uint16 //道岔ID
State byte //道岔的采集状态
Alarm byte //道岔的报警状态
Current1 byte //转辙机1电流值单位A
Current2 byte //转辙机2电流值单位A
}
type PSDState struct {
Id uint16 //屏蔽门ID
State byte //屏蔽门开关门状态
Hsjc byte //互锁解除状态
PCB byte //PCB状态
POB byte //POB状态
DPB byte //DPB状态
}
type ESBState struct {
Id uint16 //紧急停车ID
State byte //紧急停车按钮状态
PlState byte //紧急停车按钮旁路状态
ESS byte //ESS状态按下55抬起aa
}
type HoldTrainState struct {
Id uint16 //扣车ID
State byte //扣车按钮状态按下55抬起aa
PlState byte //扣车按钮旁路状态按下55抬起aa
State2 byte //2号扣车按钮状态55开启aa关闭
PlState2 byte //2号扣车按钮旁路状态55开启aa关闭
}
type SignalState struct {
Id uint16 //信号机ID
State byte //信号机的采集状态
Current byte //信号机当前亮灯的电流值
}
type AxleSectionState struct {
Id uint16 //计轴区段Id
State byte //计轴区段的采集状态
}
type WrzfState struct {
Id uint16 //无人折返ID
State byte //无人折返的采集状态
}
type FymState struct {
Id uint16 //防淹门ID
OpenAndLock byte //防淹门打开锁闭状态
CloseReq byte //防淹门关闭请求状态
}
type SPKSState struct {
Id uint16 //SPKS ID
State byte //SPKS按钮状态
PlState byte //SPKSPL按钮状态
}
type CkmState struct {
Id uint16 //车库门ID
State byte //车库门采集状态
ModeState byte //车库门模式状态
FaultState byte //车库门故障状态
}
type XcjState struct {
Id uint16 //洗车机ID
Ready byte //洗车机就绪状态
ResponseConfirm byte //回复确认状态
GoState1 byte //出发状态1
GoState2 byte //出发状态2
StopState byte //急停状态
BackConfirm byte //返回确认状态
BackMode byte //模式
}

View File

@ -0,0 +1,60 @@
package beijing11
import (
"encoding/binary"
"encoding/json"
"fmt"
"testing"
)
func TestFromInterlockFrame_Decode(t *testing.T) {
var interlockCode uint16 = 10010
var waysideCode uint16 = 10086
var data []byte
data = append(data, 0x82)
data = append(data, 0, 0) //报文长度(占位)
data = binary.BigEndian.AppendUint16(data, interlockCode)
data = binary.BigEndian.AppendUint16(data, waysideCode)
//填充设备数据
deviceCmdCount := []int{1, 1, 4, 4, 1, 1, 1, 1, 2, 1, 1}
for i, count := range deviceCmdCount {
data = binary.BigEndian.AppendUint16(data, uint16(i)) //设备数量
for j := 0; j < i; j++ {
data = binary.BigEndian.AppendUint16(data, uint16(j)) //设备ID
for k := 0; k < count; k++ {
data = append(data, byte(k)) //设备命令
}
}
}
//替换长度
len := uint16(len(data) - 3)
data[1] = byte(len >> 8)
data[2] = byte(len)
f := &FromInterlockFrame{}
err := f.Decode(data)
if err != nil {
t.Error(err)
t.FailNow()
}
marshal, _ := json.Marshal(f)
fmt.Println(marshal)
//if f.InterlockCode != interlockCode {
// t.Error()
//}
//if f.WaysideCode != waysideCode {
// t.Error()
//}
//elem := reflect.ValueOf(f).Elem()
//for i, count := range deviceCmdCount {
// field := elem.Field(3 + i)
// num := field.FieldByName("Num").Uint()
// if int(num) != count {
// t.Error()
// }
//}
}
func TestName(t *testing.T) {
}

View File

@ -444,6 +444,7 @@ func (s *VerifySimulation) GetInterlockCodes() []*config.InterlockConfig {
LocalPort: c.LocalPort,
RemotePort: c.RemotePort,
Open: c.Open,
Line: c.Line,
})
}
return configs

View File

@ -4,6 +4,7 @@ import (
"fmt"
"joylink.club/bj-rtsts-server/third_party/acc"
"joylink.club/bj-rtsts-server/third_party/btm_vobc"
"joylink.club/bj-rtsts-server/third_party/interlock/beijing11"
"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"
@ -121,7 +122,12 @@ func runThirdParty(s *memory.VerifySimulation) error {
semi_physical_train.Default().Start(s)
// 联锁启动
for _, c := range s.GetInterlockCodes() {
beijing12.Default(c).Start(s)
switch c.Line {
case "11":
beijing11.Start(c)
default:
beijing12.Default(c).Start(s)
}
}
// 计轴RSSP启动
axle_device.StartLineAllRsspAxleServices(s)
@ -152,7 +158,12 @@ func stopThirdParty(s *memory.VerifySimulation) {
semi_physical_train.Default().Stop()
// 联锁启动
for _, c := range s.GetInterlockCodes() {
beijing12.Default(c).Stop()
switch c.Line {
case "11":
beijing11.Stop()
default:
beijing12.Default(c).Stop()
}
}
//计轴RSSP启动销毁
axle_device.StopLineAllRsspAxleServices()