[新增]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 ( import (
"flag" "flag"
"fmt" "fmt"
"log/slog"
"github.com/spf13/viper" "github.com/spf13/viper"
"joylink.club/iot/dto" "joylink.club/iot/dto"
"log/slog"
) )
const ( const (
@ -126,6 +125,7 @@ type InterlockConfig struct {
RemotePort int `json:"remotePort" description:"远端接收采集信息端口"` RemotePort int `json:"remotePort" description:"远端接收采集信息端口"`
Open bool `json:"open" description:"是否开启"` Open bool `json:"open" description:"是否开启"`
Code string `json:"code" description:"所属集中站"` Code string `json:"code" description:"所属集中站"`
Line string `json:"line" description:"联锁通信协议所属线路"`
} }
type ElectricMachineryConfig struct { type ElectricMachineryConfig struct {
Ip string `json:"ip" description:"IP配置"` 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 package beijing11
import ( import (
"encoding/json"
"fmt" "fmt"
"joylink.club/bj-rtsts-server/config" "joylink.club/bj-rtsts-server/config"
"joylink.club/bj-rtsts-server/third_party/udp" "joylink.club/bj-rtsts-server/third_party/udp"
@ -11,116 +12,52 @@ import (
var ( var (
initMutex = sync.Mutex{} initMutex = sync.Mutex{}
interlockMap = make(map[string]*InterlockProxy)
logTag = "[北京11号线联锁通信]" logTag = "[北京11号线联锁通信]"
running = false
server udp.UdpServer
) )
func GetInstant(c *config.InterlockConfig, msgManager MsgManager) *InterlockProxy { func Start(interlockConfig *config.InterlockConfig) {
initMutex.Lock() if interlockConfig == nil || interlockConfig.Ip == "" || !interlockConfig.Open {
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 {
return return
} }
if i.running { if running {
panic("启动联锁消息服务错误: 存在正在运行的任务") return
}
initMutex.Lock()
defer initMutex.Unlock()
if running {
return
} }
//UDP通信设施 //UDP通信设施
i.server = udp.NewServer(fmt.Sprintf(":%d", i.runConfig.LocalPort), i.handleUpperData) server = udp.NewServer(fmt.Sprintf(":%d", interlockConfig.LocalPort), func(b []byte) {
err := i.server.Listen() slog.Info(fmt.Sprintf("%s收到消息%x", logTag, b))
frame := &FromInterlockFrame{}
err := frame.Decode(b)
if err != nil { if err != nil {
panic("启动联锁消息服务错误无法启动UDP服务") 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)))
} }
i.running = true }
i.client = udp.NewClient(fmt.Sprintf("%v:%v", i.runConfig.Ip, i.runConfig.RemotePort)) })
err := server.Listen()
if err != nil {
panic(fmt.Sprintf("%s启动UDP服务失败%s", logTag, err))
}
running = true
} }
// 处理来自上位机的数据 func Stop() {
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() {
initMutex.Lock() initMutex.Lock()
defer initMutex.Unlock() defer initMutex.Unlock()
delete(interlockMap, i.runConfig.Code) running = false
if i.client != nil { if server != nil {
i.client.Close() server.Close()
server = nil
} }
if i.server != nil {
i.server.Close()
}
i.running = false
} }

View File

@ -3,84 +3,337 @@ package beijing11
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"hash/crc32"
"joylink.club/bj-rtsts-server/sys_error" "joylink.club/bj-rtsts-server/sys_error"
) )
type BaseStruct struct { // FromInterlockFrame 来自联锁的数据帧
ID byte type FromInterlockFrame struct {
ControlWord byte Len uint16
Data []byte 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 { func (f *FromInterlockFrame) Decode(data []byte) error {
if buf[0] != FrameHead { f.Len = binary.BigEndian.Uint16(data[1:3])
return sys_error.New(logTag + "帧头不正确:" + fmt.Sprintf("%02X", buf[0])) 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 { f.InterlockCode = binary.BigEndian.Uint16(data[3:5])
return sys_error.New(logTag + "帧尾不正确:" + fmt.Sprintf("%02X", buf[len(buf)-1])) f.WaysideCode = binary.BigEndian.Uint16(data[5:7])
}
crc := crc32.ChecksumIEEE(buf[1 : len(buf)-5]) turnoutData, i := decodeDeviceData(data, 7)
if crc != binary.BigEndian.Uint32(buf[len(buf)-5:len(buf)-1]) { f.TurnoutData = turnoutData
return sys_error.New(logTag + "CRC校验失败") psdData, i := decodeDeviceData(data, i)
} f.PSDData = psdData
m.ID = buf[1] esbData, i := decodeESBData(data, i)
m.ControlWord = buf[2] f.ESBData = esbData
m.Data = buf[5 : len(buf)-5] 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 return nil
} }
func (m *BaseStruct) Encode() []byte { func decodeDeviceData(data []byte, index uint16) (*DeviceData, uint16) {
var data []byte data = data[index:]
data = append(data, FrameHead) num := binary.BigEndian.Uint16(data[0:2])
data = append(data, m.ID) i := 2 + 3*num
data = append(data, m.ControlWord) data = data[2:i]
data = binary.BigEndian.AppendUint16(data, uint16(len(m.Data))) var cmdList []*DeviceCmd
data = append(data, m.Data...) for i := 0; i < len(data); i += 3 {
crc := crc32.ChecksumIEEE(data[1:]) cmdList = append(cmdList, &DeviceCmd{
data = binary.BigEndian.AppendUint32(data, crc) Id: binary.BigEndian.Uint16(data[i : i+2]),
data = append(data, FrameTail) Cmd: data[i+2],
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的倍数")
} }
for i := 0; i < len(buf); i += 2 { deviceData := &DeviceData{
r.RegisterAddr = append(r.RegisterAddr, binary.BigEndian.Uint16(buf[i:i+2])) Num: num,
CmdList: cmdList,
} }
return nil return deviceData, index + i
} }
type ReadRegisterRes struct { func decodeESBData(data []byte, index uint16) (*ESBData, uint16) {
RegisterData [][]byte //寄存器数据 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 { func decodeHoldTrainData(data []byte, index uint16) (*HoldTrainData, uint16) {
var data []byte data = data[index:]
for _, datum := range r.RegisterData { num := binary.BigEndian.Uint16(data[0:2])
data = append(data, datum...) 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 { func decodeSPKSData(data []byte, index uint16) (*SPKSData, uint16) {
RegisterAddr []uint16 //寄存器地址 data = data[index:]
RegisterData [][]byte //寄存器数据 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 { type DeviceData struct {
if len(buf)%6 != 0 { Num uint16 //设备数量
return sys_error.New(logTag + "写寄存器数据字节长度不是6的倍数") CmdList []*DeviceCmd
} }
for i := 0; i < len(buf); i += 6 {
w.RegisterAddr = append(w.RegisterAddr, binary.BigEndian.Uint16(buf[i:i+2])) type ESBData struct {
w.RegisterData = append(w.RegisterData, buf[i+2:i+6]) Num uint16 //紧急停车按钮的数量
} CmdList []*ESBCmd
return nil }
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, LocalPort: c.LocalPort,
RemotePort: c.RemotePort, RemotePort: c.RemotePort,
Open: c.Open, Open: c.Open,
Line: c.Line,
}) })
} }
return configs return configs

View File

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