271 lines
9.2 KiB
Go
271 lines
9.2 KiB
Go
|
package semi_physical_train
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/hex"
|
||
|
"fmt"
|
||
|
"github.com/google/uuid"
|
||
|
"joylink.club/bj-rtsts-server/config"
|
||
|
"joylink.club/bj-rtsts-server/const/balise_const"
|
||
|
"joylink.club/bj-rtsts-server/dto/state_proto"
|
||
|
"joylink.club/bj-rtsts-server/third_party/can_btm"
|
||
|
"joylink.club/bj-rtsts-server/third_party/message"
|
||
|
"joylink.club/bj-rtsts-server/third_party/udp"
|
||
|
"log/slog"
|
||
|
"math"
|
||
|
"time"
|
||
|
|
||
|
"strings"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
type BtmVobcManage interface {
|
||
|
GetBtmVobcConfig() config.BtmVobcConfig
|
||
|
|
||
|
GetAllTrain() []*state_proto.TrainState
|
||
|
GetConnVobcTrain() *state_proto.TrainState
|
||
|
}
|
||
|
type BtmVobcService interface {
|
||
|
Start(btmVobcManage BtmVobcManage)
|
||
|
Stop()
|
||
|
SendData(data []byte)
|
||
|
}
|
||
|
type BtmVobcClient struct {
|
||
|
calFun context.CancelFunc
|
||
|
client udp.UdpClient
|
||
|
server udp.UdpServer
|
||
|
manage BtmVobcManage
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
btmVobcLocker sync.Mutex
|
||
|
btmVobcClient *BtmVobcClient
|
||
|
btmVobcBaliseLocker sync.Mutex
|
||
|
//最新接受数据时间
|
||
|
reviceTimeStamp int64
|
||
|
)
|
||
|
|
||
|
func BtmDefault() BtmVobcService {
|
||
|
defer btmVobcLocker.Unlock()
|
||
|
btmVobcLocker.Lock()
|
||
|
if btmVobcClient == nil {
|
||
|
btmVobcClient = &BtmVobcClient{}
|
||
|
}
|
||
|
return btmVobcClient
|
||
|
}
|
||
|
|
||
|
func (b *BtmVobcClient) Start(btmVobcManage BtmVobcManage) {
|
||
|
cfg := btmVobcManage.GetBtmVobcConfig()
|
||
|
if !cfg.Open {
|
||
|
slog.Info("11号线 btm vobc配置未开启...")
|
||
|
return
|
||
|
}
|
||
|
udpServer := udp.NewServer(fmt.Sprintf("%v:%d", cfg.LocalUdpIp, cfg.LocalUdpPort), b.handleBtmVobcFrames)
|
||
|
err := udpServer.Listen()
|
||
|
if err != nil {
|
||
|
slog.Error("11号线 btm VOBC 服务启动失败...")
|
||
|
return
|
||
|
}
|
||
|
//
|
||
|
udpClient := udp.NewClient(fmt.Sprintf("%s:%d", cfg.RemoteIp, cfg.RemoteUdpPort))
|
||
|
b.manage = btmVobcManage
|
||
|
b.server = udpServer
|
||
|
b.client = udpClient
|
||
|
reviceTimeStamp = time.Now().UnixMilli()
|
||
|
_, calFun := context.WithCancel(context.Background())
|
||
|
b.calFun = calFun
|
||
|
}
|
||
|
|
||
|
func (b *BtmVobcClient) vobcBtmQueryHandle(data []byte) {
|
||
|
train := b.manage.GetConnVobcTrain()
|
||
|
if train == nil {
|
||
|
slog.Error("vobc btm 未找到VOBC连接的列车...")
|
||
|
return
|
||
|
}
|
||
|
requestId := uuid.New().String()
|
||
|
frameType, dataText, err := message.BtmVobcDecode(data)
|
||
|
if err != nil {
|
||
|
slog.Error(fmt.Sprintf("%v,请求id:%v", err, requestId))
|
||
|
return
|
||
|
}
|
||
|
receiveDataTime := time.Now().UnixMilli()
|
||
|
if frameType == message.COMMAND_TYPE {
|
||
|
idCommand := &message.BtmVobcIdCommand{}
|
||
|
idCommand.Decode(dataText)
|
||
|
slog.Info(fmt.Sprintf("成功接受btm vobc的id命令帧,requestId:%v,接受时间:%v", requestId, receiveDataTime))
|
||
|
if train.VobcState.VobcBtmInfo == nil {
|
||
|
train.VobcState.VobcBtmInfo = &state_proto.TrainVobcState_VobcBtmInfo{BtmId: uint32(idCommand.BtmId), VobcId: uint32(idCommand.VobcId), VobcLifeId: idCommand.VobcLifeNum}
|
||
|
}
|
||
|
} else if train.VobcState.VobcBtmInfo != nil && frameType == message.REQUEST_TYPE {
|
||
|
slog.Info(fmt.Sprintf("成功接受btm vobc的请求帧,requestId:%v,接受时间(微秒):%v", requestId, receiveDataTime))
|
||
|
req := &message.BtmVobcReq{}
|
||
|
req.Decode(dataText)
|
||
|
b.requestFramePackets(requestId, req, train, receiveDataTime)
|
||
|
|
||
|
} else {
|
||
|
//slog.Error(fmt.Sprintf("btm vobc 解析未知命令帧类型:0x%v,原始数据:%v,长度:%v,requestId:%v", strconv.FormatInt(int64(frameType), 16), hex.EncodeToString(cfs), len(cfs), requestId))
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *BtmVobcClient) handleBtmVobcFrames(cfs []byte) {
|
||
|
/* reviceTimeStamp = time.Now().UnixMilli()
|
||
|
train := b.manage.GetConnVobcTrain()
|
||
|
if train == nil {
|
||
|
//slog.Error("vobc btm 未找到VOBC连接的列车...")
|
||
|
return
|
||
|
}
|
||
|
requestId := uuid.New().String()
|
||
|
slog.Info(fmt.Sprintf("获取到vobc btm原始数据:%v, requestId:%v", hex.EncodeToString(cfs), requestId))
|
||
|
frameType, dataText, err := message.BtmVobcDecode(cfs)
|
||
|
if err != nil {
|
||
|
slog.Error(fmt.Sprintf("%v,请求id:%v", err, requestId))
|
||
|
return
|
||
|
}
|
||
|
receiveDataTime := time.Now().UnixMicro()
|
||
|
decodePayMicoTime := (receiveDataTime - receiveDataTime) / 100
|
||
|
if frameType == message.COMMAND_TYPE {
|
||
|
idCommand := &message.BtmVobcIdCommand{}
|
||
|
idCommand.Decode(dataText)
|
||
|
slog.Info(fmt.Sprintf("成功接受btm vobc的id命令帧,requestId:%v,接受时间(微秒):%v", requestId, receiveDataTime))
|
||
|
b.packets(requestId, idCommand.VobcLifeNum, idCommand.AutoIdFrame, receiveDataTime, decodePayMicoTime, train.VobcBtm)
|
||
|
} else if frameType == message.REQUEST_TYPE {
|
||
|
slog.Info(fmt.Sprintf("成功接受btm vobc的请求帧,requestId:%v,接受时间(微秒):%v", requestId, receiveDataTime))
|
||
|
req := &message.BtmVobcReq{}
|
||
|
req.Decode(dataText)
|
||
|
b.RequestFramePackets(req.VobcLifeNum, req.AutoIdFrame, requestId, receiveDataTime, decodePayMicoTime, req, train.VobcBtm)
|
||
|
} else {
|
||
|
slog.Error(fmt.Sprintf("btm vobc 解析未知命令帧类型:0x%v,原始数据:%v,长度:%v,requestId:%v", strconv.FormatInt(int64(frameType), 16), hex.EncodeToString(cfs), len(cfs), requestId))
|
||
|
return
|
||
|
}*/
|
||
|
}
|
||
|
func createFreeBalisePacketString() string {
|
||
|
return strings.Repeat("00", balise_const.UserTelegramByteLen)
|
||
|
}
|
||
|
func createFreeBalisePackets() []byte {
|
||
|
str := createFreeBalisePacketString()
|
||
|
d, _ := hex.DecodeString(str)
|
||
|
return d
|
||
|
}
|
||
|
|
||
|
// 请求帧
|
||
|
func (b *BtmVobcClient) requestFramePackets(requestId string, req *message.BtmVobcReq, train *state_proto.TrainState, receiveTime int64) {
|
||
|
|
||
|
if req.FrameStatus == message.REQ_FRAME_STATUS_BOOT && req.MessageType == message.REQ_PACKETS_TYPE_BOOT {
|
||
|
//vobc 上电,清空应答器
|
||
|
can_btm.ClearBalise(train)
|
||
|
} else {
|
||
|
var (
|
||
|
btmState *state_proto.BTMState
|
||
|
dsn byte
|
||
|
matcher bool
|
||
|
)
|
||
|
ms := req.MessageSerial
|
||
|
|
||
|
if req.FrameStatus == message.REQ_FRAME_STATUS_ERROR {
|
||
|
//抢答器错误,重新发送
|
||
|
btmState, dsn, _, _ = can_btm.FindBaliseResend(train)
|
||
|
} else {
|
||
|
//判断 报文序列号与之前发送的 是否一致,不一致
|
||
|
//如果一致返回新的应答器,如果不一致返回之前发送的应答器,如果不一致并且没有找到之前发送的应答器,则返回新应答器
|
||
|
btmState, dsn, matcher = can_btm.FindBaliseByMessageSerial(train, req.MessageSerial)
|
||
|
if matcher {
|
||
|
if btmState != nil {
|
||
|
//正常应答
|
||
|
btmState.IsSend = true
|
||
|
ms = req.MessageSerial + 1
|
||
|
if ms > 255 {
|
||
|
ms = 1
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
//vobc 未收到应答器,重新发送
|
||
|
}
|
||
|
}
|
||
|
b.packets(requestId, btmState, ms, dsn, req.VobcLifeNum, receiveTime)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 有应答器报文
|
||
|
func (b *BtmVobcClient) balisePackets(telegram, requestId string, messageSerial, dsn byte, vobcLifeNum uint32, receiveTime int64) {
|
||
|
|
||
|
data, e := hex.DecodeString(telegram)
|
||
|
if e != nil {
|
||
|
slog.Error(fmt.Sprintf("解析应答器报文失败应答器报文长度:%v", telegram), e)
|
||
|
return
|
||
|
}
|
||
|
//前沿时间
|
||
|
var fttl uint16 = 0
|
||
|
tmpFttl := int64(math.Abs(float64(time.Now().UnixMilli() - receiveTime)))
|
||
|
if tmpFttl >= 0xffff {
|
||
|
fttl = 0xffff
|
||
|
} else {
|
||
|
fttl = uint16(tmpFttl)
|
||
|
}
|
||
|
|
||
|
var bttl uint16 = 0
|
||
|
tmpBttl := int64(math.Abs(float64(time.Now().UnixMilli() - receiveTime)))
|
||
|
if tmpBttl >= 0xffff {
|
||
|
bttl = 0xffff
|
||
|
} else {
|
||
|
bttl = uint16(tmpBttl)
|
||
|
}
|
||
|
repTimeMicro := (time.Now().UnixMilli() - receiveTime) / 10
|
||
|
baliseMsg := &message.BtmVobcMessage{FontTtl: fttl, BtmStatus: message.BTM_STSTUS_NORMAL, DecodeTime: uint16(repTimeMicro),
|
||
|
BackTtl: bttl, ResponseTime: byte(repTimeMicro), MsgSerial: messageSerial, BtmMsg: data,
|
||
|
VobcLifeNum: vobcLifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: dsn}}
|
||
|
baliseData := baliseMsg.Encode()
|
||
|
slog.Info(fmt.Sprintf("发送btm vobc 报文数据 报文序列id:%v 报文内容:%X 长度:%v ,requestId:%v", messageSerial, baliseData, len(baliseData), requestId))
|
||
|
err := b.client.Send(baliseData)
|
||
|
if err != nil {
|
||
|
slog.Error(fmt.Sprintf("发送btm vobc 报文数据 报文序列id:%v 报文内容:%X 长度:%v ,requestId:%v", messageSerial, baliseData, len(baliseData), requestId), err)
|
||
|
return
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// 无应答器报文
|
||
|
func (b *BtmVobcClient) balisePacketsFree(requestId string, receiveTime int64, vobcLifeNum uint32, autoCommandId, messageSerial byte) {
|
||
|
repTimeMicro := (time.Now().UnixMicro() - receiveTime) / 10
|
||
|
freeMsg := &message.BtmVobcMsgFree{BtmStatus: message.BTM_STSTUS_NORMAL, Fun1: 0xffff, Fun2: 0x00CF, Fun3: uint16(0), Fun4: uint16(0),
|
||
|
FreeMsg: createFreeBalisePackets(), RespTime: byte(repTimeMicro), MsgSerial: messageSerial, VobcLifeNum: vobcLifeNum, BaseBtmVobc: message.BaseBtmVobc{AutoIdFrame: autoCommandId}}
|
||
|
freeData := freeMsg.Encode()
|
||
|
logStr := fmt.Sprintf("发送btm vobc 报文序列id:%v 空闲帧报文:%X 长度:%v ,requestId:%v", messageSerial, freeData, len(freeData), requestId)
|
||
|
slog.Info(logStr)
|
||
|
err := b.client.Send(freeData)
|
||
|
if err != nil {
|
||
|
slog.Error(logStr, err)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 应答器报文或空报文
|
||
|
func (b *BtmVobcClient) packets(requestId string, btmState *state_proto.BTMState, messageSerial, dsn byte, vobcLifeNum uint32, receiveTime int64) {
|
||
|
if btmState == nil {
|
||
|
b.balisePacketsFree(requestId, receiveTime, vobcLifeNum, dsn, messageSerial)
|
||
|
} else {
|
||
|
b.balisePackets(btmState.Telegram, requestId, messageSerial, dsn, vobcLifeNum, receiveTime)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *BtmVobcClient) SendData(data []byte) {
|
||
|
if b.client != nil {
|
||
|
slog.Info(fmt.Sprintf("发送btm vobc 报文:%v 长度:%v", hex.EncodeToString(data), len(data)))
|
||
|
err := b.client.Send(data)
|
||
|
if err != nil {
|
||
|
slog.Error("发送btm vobc 报文失败:", err)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
func (b *BtmVobcClient) Stop() {
|
||
|
if b.server != nil {
|
||
|
b.calFun()
|
||
|
b.server.Close()
|
||
|
}
|
||
|
if b.client != nil {
|
||
|
b.client.Close()
|
||
|
}
|
||
|
}
|